PowerVR GPU-MMU

2025-09-23 117 0

GPU MMU框架和页表格式分析

整体架构

系统架构图

架构层次

应用层
    ↓
设备内存管理层 (devicemem)
    ↓
MMU通用管理层 (mmu_common)
    ↓
物理内存资源层 (PMR) + 设备特定层 (device)
    ↓
物理内存堆 (PhysHeap)

核心组件关系

MMU_CONTEXT
    ├── MMU_DEVICEATTRIBS (设备属性)
    ├── MMU_PHYSMEM_CONTEXT (物理内存上下文)
    │   ├── RA_ARENA (资源分配器)
    │   ├── MMU_CTX_CLEANUP_DATA (清理数据)
    │   └── MMU_MEMORY_MAPPING (内存映射)
    └── MMU_Levelx_INFO (多级页表信息)
        ├── MMU_MEMORY_DESC (内存描述符)
        └── apsNextLevel[] (下级页表数组)

核心数据结构关系

页表层次结构

多级页表设计

Level 3 (PC - Page Catalogue)     <- 顶级页表
    ↓
Level 2 (PD - Page Directory)     <- 中级页表  
    ↓
Level 1 (PT - Page Table)         <- 叶子页表 (PTE指向数据页)
    ↓
Level 0 (Data Pages)              <- 实际数据页面

虚拟地址分解

虚拟地址位字段分解:
┌─────────┬─────────┬─────────┬──────────────┐
│ PC Index│ PD Index│ PT Index│ Page Offset  │
│         │         │         │              │
└─────────┴─────────┴─────────┴──────────────┘

地址计算函数
- _CalcPCEIdx(): 计算页目录索引
- _CalcPDEIdx(): 计算页表目录索引
- _CalcPTEIdx(): 计算页表项索引

页表项格式

通用页表项结构

typedef struct _MMU_PxE_CONFIG_ {
    IMG_UINT32 uiBytesPerEntry;      // 页表项大小 (4字节或8字节)
    IMG_UINT32 uiAddrLog2Align;      // 地址对齐位数
    IMG_UINT32 uiAddrShift;          // 地址移位量
    IMG_UINT64 uiAddrMask;           // 地址掩码
    IMG_UINT64 uiProtMask;           // 保护位掩码
    IMG_UINT64 uiValidEnMask;        // 有效位掩码
    IMG_UINT64 uiPendingEnMask;      // 挂起位掩码
    IMG_UINT64 uiParityBitMask;      // 奇偶校验位掩码
    IMG_UINT32 uiParityBitShift;     // 奇偶校验位移位
    const IMG_CHAR *pszPxLevelStr;   // 级别名称字符串
} MMU_PxE_CONFIG;

64位页表项格式

64位页表项位字段布局:

parity Physical Address
(对齐后的物理地址)
Protection
Flags
Valid Pending Reserv
63 ... ... 2 1 0

字段说明
- Physical Address: 对齐后的物理地址,右移后左移去除低位
- Protection Flags: 读写权限、缓存属性等
- Valid: 页表项是否有效
- Pending: 页面是否正在加载(用于页面换入换出)
- Parity: 奇偶校验位,用于错误检测

32位页表项格式

32位页表项位字段布局:

Physical Address
(对齐后的物理地址)
Protection
Flags
Valid Reserv
31 1 0

页表项构造过程

// 64位页表项构造
ui64PxE64 = uiAddr                    // 物理地址
         >> psConfig->uiAddrLog2Align // 右移去除对齐位
         << psConfig->uiAddrShift     // 左移到正确位置
         & psConfig->uiAddrMask;      // 应用地址掩码

ui64PxE64 |= uiProtFlags;             // 添加保护标志

// 添加奇偶校验位
if (psConfig->uiParityBitMask) {
    ui64PxE64 |= _GetParityBit(psDevVAddr->uiAddr ^ psDevPAddr->uiAddr) 
                 << psConfig->uiParityBitShift;
}

设备特定配置

MMU设备属性结构

typedef struct _MMU_DEVICEATTRIBS_ {
    const MMU_PxE_CONFIG *psBaseConfig;           // 基础级别配置
    const MMU_DEVVADDR_CONFIG *psTopLevelDevVAddrConfig; // 顶级虚拟地址配置

    // 设备特定的保护位计算函数
    IMG_UINT32 (*pfnDerivePCEProt4)(IMG_UINT32 uiProtFlags);
    IMG_UINT64 (*pfnDerivePCEProt8)(IMG_UINT32 uiProtFlags, IMG_UINT32 uiLog2DataPageSize);
    IMG_UINT32 (*pfnDerivePDEProt4)(IMG_UINT32 uiProtFlags);
    IMG_UINT64 (*pfnDerivePDEProt8)(IMG_UINT32 uiProtFlags, IMG_UINT32 uiLog2DataPageSize);
    IMG_UINT32 (*pfnDerivePTEProt4)(IMG_UINT32 uiProtFlags);
    IMG_UINT64 (*pfnDerivePTEProt8)(IMG_UINT32 uiProtFlags, IMG_UINT32 uiLog2DataPageSize);

    // 页面大小配置函数
    PVRSRV_ERROR (*pfnGetPageSizeConfiguration)(IMG_UINT32 uiLog2DataPageSize,
                                                const MMU_PxE_CONFIG **ppsMMUPDEConfig,
                                                const MMU_PxE_CONFIG **ppsMMUPTEConfig,
                                                const MMU_DEVVADDR_CONFIG **ppsMMUDevVAddrConfig,
                                                IMG_HANDLE *phPriv);

    MMU_TYPE eMMUType;                            // MMU类型
    const IMG_CHAR *pszMMUPxPDumpMemSpaceName;    // PDump内存空间名称
} MMU_DEVICEATTRIBS;

虚拟地址配置

typedef struct _MMU_DEVVADDR_CONFIG_ {
    IMG_UINT64 uiPCIndexMask;        // PC索引掩码
    IMG_UINT32 uiPCIndexShift;       // PC索引移位
    IMG_UINT32 uiNumEntriesPC;       // PC条目数

    IMG_UINT64 uiPDIndexMask;        // PD索引掩码  
    IMG_UINT32 uiPDIndexShift;       // PD索引移位
    IMG_UINT32 uiNumEntriesPD;       // PD条目数

    IMG_UINT64 uiPTIndexMask;        // PT索引掩码
    IMG_UINT32 uiPTIndexShift;       // PT索引移位  
    IMG_UINT32 uiNumEntriesPT;       // PT条目数

    IMG_UINT64 uiPageOffsetMask;     // 页面偏移掩码
    IMG_UINT64 uiOffsetInBytes;      // 字节偏移
} MMU_DEVVADDR_CONFIG;

页面大小支持

多页面大小架构

支持的页面大小:
├── 4KB   (标准页面大小)
├── 16KB  (中等页面大小)
├── 64KB  (大页面大小)
├── 2MB   (超大页面大小)
└── 1GB   (巨大页面大小,某些架构)

页面大小配置获取

// 根据页面大小获取对应的MMU配置
eError = psDevAttrs->pfnGetPageSizeConfiguration(
    uiLog2DataPageSize,           // 输入: 页面大小(log2)
    &psMMUPDEConfig,             // 输出: PDE配置
    &psMMUPTEConfig,             // 输出: PTE配置  
    &psMMUDevVAddrConfig,        // 输出: 虚拟地址配置
    &hPriv                       // 输出: 私有句柄
);

特殊页面类型

后备页面系统

后备页面类型:
├── SCRATCH_PAGE (临时页面)
│   ├── 用于稀疏内存的占位符
│   ├── 读写权限
│   └── 初始化值: 0
└── DEV_ZERO_PAGE (零页面)
    ├── 用于零内存区域
    ├── 只读权限
    └── 初始化值: 0

页面状态标识

// 页表项状态组合
enum {
    MMU_INVALID   = 0,  // 00: 无效
    MMU_VALID     = 1,  // 01: 有效
    MMU_PENDING   = 2,  // 10: 挂起
    MMU_INCONSIST = 3   // 11: 不一致(挂起+有效)
};

内存映射流程

地址转换流程

虚拟地址 → 物理地址转换:
1. 提取PC索引 → 查找PC表项 → 获取PD基址
2. 提取PD索引 → 查找PD表项 → 获取PT基址  
3. 提取PT索引 → 查找PT表项 → 获取页面基址
4. 页面基址 + 页内偏移 → 最终物理地址

页表操作类型

页表操作:
├── 分配操作 (_MMU_AllocLevel)
│   ├── 递归分配多级页表
│   ├── 按需创建下级页表
│   └── 引用计数管理
├── 释放操作 (_MMU_FreeLevel)  
│   ├── 递归释放页表
│   ├── 引用计数递减
│   └── 延迟物理内存释放
└── 映射操作 (MMU_MapPages/MMU_MapPMRFast)
    ├── 设置页表项
    ├── 缓存管理
    └── TLB失效

优化特性

快速映射路径

快速映射优化:
├── 批量地址获取 (减少PMR调用)
├── 直接页表项写入 (避免函数调用开销)
├── 配置参数缓存 (避免重复计算)
├── 分块处理大范围映射
└── 重映射策略控制

缓存一致性管理

缓存管理层次:
├── CPU缓存 (PhysHeapPagesClean)
├── GPU MMU缓存 (pfnMMUCacheInvalidate)  
├── TLB刷新 (MMU_LEVEL_1失效)
└── 延迟释放 (等待缓存失效完成)

调试和分析支持

故障诊断信息

typedef struct _MMU_FAULT_DATA_ {
    MMU_FAULT_TYPE eType;                    // 故障类型
    MMU_LEVEL eTopLevel;                     // 顶级页表类型
    MMU_LEVEL_DATA sLevelData[MMU_MAX_LEVEL]; // 各级页表数据
} MMU_FAULT_DATA;

typedef struct _MMU_LEVEL_DATA_ {
    IMG_UINT32 ui32Index;           // 页表项索引
    IMG_UINT32 ui32NumOfEntries;    // 页表总条目数
    IMG_UINT32 uiBytesPerEntry;     // 每个条目的字节数
    IMG_UINT64 ui64Address;         // 页表项内容
    const IMG_CHAR *psDebugStr;     // 调试字符串
} MMU_LEVEL_DATA;

PDump集成

PDump支持:
├── 符号地址生成 (MMU_ContextDerivePCPDumpSymAddr)
├── 页表内容转储 (PDumpMMUDumpPxEntries)
├── MMU上下文管理 (PDUMP_MMU_ALLOC_MMUCONTEXT)
└── 连续转储支持 (PDUMP_FLAGS_CONTINUOUS)

GPU MMU代码详细分析

关键宏定义分析

核心宏定义

#define SCRATCH_PAGE 1
#define DEV_ZERO_PAGE 2
#define PVR_SCRATCH_PAGE_INIT_VALUE 0
#define PVR_ZERO_PAGE_INIT_VALUE  0

关键分析: 定义两种特殊页面类型
- SCRATCH_PAGE: 临时页面,用于稀疏内存分配时的占位符
- DEV_ZERO_PAGE: 零页面,用于只读零内存区域
- 这两种特殊页面是GPU MMU管理稀疏内存的核心机制

数据结构定义

MMU清理数据结构

typedef struct _MMU_CTX_CLEANUP_DATA_
{
    ATOMIC_T iRef;                           // 引用计数
    POS_LOCK hCleanupLock;                   // 清理锁
    DLLIST_NODE sMMUCtxCleanupItemsHead;     // 清理项链表
    IMG_BOOL bMMUContextExists;              // MMU上下文是否存在
#if defined(SUPPORT_CUSTOM_OSID_EMISSION)
    IMG_UINT32 ui32OSid;                     // 操作系统ID
#endif
} MMU_CTX_CLEANUP_DATA;

设计要点: 管理延迟释放机制,页表不能立即释放,需要等待MMU cache失效
- 引用计数: 确保在所有清理任务完成前不会释放元数据,使用引用计数确保清理数据生命周期
- 清理锁: 保护并发访问
- 存在标志: 防止访问已销毁的MMU上下文

MMU清理项

typedef struct _MMU_CLEANUP_ITEM_
{
    PVRSRV_CLEANUP_THREAD_WORK sCleanupThreadFn;  // 清理线程工作项
    DLLIST_NODE sMMUMappingHead;                   // 待释放的内存映射
    DLLIST_NODE sMMUCtxCleanupItem;                // 在上下文中的节点
    MMU_CTX_CLEANUP_DATA *psMMUCtxCleanupData;     // 清理元数据引用
    PVRSRV_CLIENT_SYNC_PRIM *psSync;               // 同步原语
    IMG_UINT32 uiRequiredSyncVal;                  // 所需同步值
    IMG_UINT32 uiRequiredPowerOffCounter;          // 电源关闭计数器
    PVRSRV_DEVICE_NODE *psDevNode;                 // 设备节点
} MMU_CLEANUP_ITEM;

关键机制:
- 延迟释放: 等待MMU缓存失效后再释放内存
- 同步原语: 确保GPU完成相关操作
- 电源状态: 考虑设备电源状态

工作流程

物理内存上下文

typedef struct _MMU_PHYSMEM_CONTEXT_
{
    struct _MMU_CONTEXT_ *psMMUContext;      // 关联的MMU上下文
    PVRSRV_DEVICE_NODE *psDevNode;           // 父设备节点
    IMG_UINT32 uiNumAllocations;             // 分配计数
    RA_ARENA *psPhysMemRA;                   // 物理内存资源分配器
    IMG_CHAR *pszPhysMemRAName;              // RA名称
    size_t uiPhysMemRANameAllocSize;         // 名称缓冲区大小
    MMU_CTX_CLEANUP_DATA *psCleanupData;     // 清理元数据
    DLLIST_NODE sTmpMMUMappingHead;          // 临时映射列表
#if defined(SUPPORT_CUSTOM_OSID_EMISSION)
    IMG_UINT32 ui32OSid;                     // OS ID
    IMG_UINT32 ui32OSidReg;                  // OS ID寄存器值
    IMG_BOOL   bOSidAxiProt;                 // AXI保护位
#endif
} MMU_PHYSMEM_CONTEXT;

架构要点: RA Arena机制:资源分配器(Resource Allocator),管理页表内存的分配和释放,支持导入/导出回调,提供内存碎片管理
- RA分配器: 使用Resource Allocator管理物理内存
- OS隔离: 支持多OS环境下的内存隔离
- 临时列表: 用于批量处理内存映射

内存映射结构

typedef struct _MMU_MEMORY_MAPPING_
{
    MMU_PHYSMEM_CONTEXT *psContext;          // 物理内存上下文
    PG_HANDLE sMemHandle;                    // OS/系统句柄
    void *pvCpuVAddr;                        // CPU虚拟地址
    IMG_DEV_PHYADDR sDevPAddr;               // 设备物理地址
    size_t uiSize;                           // 大小
    IMG_UINT32 uiCpuVAddrRefCount;           // CPU虚拟地址引用计数
    DLLIST_NODE sMMUMappingItem;             // 链表节点
} MMU_MEMORY_MAPPING;

内存管理策略: 引用计数机制,多个MMU_MEMORY_DESC可以共享同一个MAPPING,只有引用计数为0时才真正释放
- 双地址空间: 同时管理CPU和设备地址
- 引用计数: 支持多次映射同一物理内存
- 句柄抽象: 封装OS特定的内存管理

内存描述符

typedef struct _MMU_MEMORY_DESC_
{
    IMG_BOOL bValid;                         // 是否有效
    IMG_DEV_PHYADDR sDevPAddr;              // 设备物理地址
    void *pvCpuVAddr;                       // CPU虚拟地址
    MMU_MEMORY_MAPPING *psMapping;          // 映射信息
    IMG_UINT32 uiOffset;                    // 在mapping中的偏移
    IMG_UINT32 uiSize;                      // 大小
} MMU_MEMORY_DESC;

设计特点:
- 有效性标志: 支持"空"描述符的批量分配
- 偏移机制: 一个mapping可以对应多个描述符
- 分离关注点: 描述符和映射分离

页表级别信息

typedef struct _MMU_Levelx_INFO_
{
    IMG_UINT32 ui32NumOfEntries;             // 条目数量
    IMG_UINT32 ui32RefCount;                 // 引用计数
    MMU_MEMORY_DESC sMemDesc;                // 内存描述符
    struct _MMU_Levelx_INFO_ *apsNextLevel[IMG_FLEX_ARRAY_MEMBER]; // 下一级页表数组(柔性数组成员)
} MMU_Levelx_INFO;

通用设计:
- 柔性数组: 支持不同级别的页表大小
- 引用计数: 精确的生命周期管理
- 递归结构: 支持多级页表遍历

层次结构

Level 3 (PC): 页目录指针表
    ├── Level 2 (PD): 页目录
    │   ├── Level 1 (PT): 页表
    │   │   ├── PTE 0 -> 物理页0
    │   │   ├── PTE 1 -> 物理页1
    │   │   └── ...
    │   └── ...
    └── ...

MMU上下文主结构

struct _MMU_CONTEXT_
{
    CONNECTION_DATA *psConnection;           // 连接数据
    MMU_DEVICEATTRIBS *psDevAttrs;          // 设备属性
    struct _MMU_PHYSMEM_CONTEXT_ *psPhysMemCtx; // 物理内存上下文
#if defined(PDUMP)
    IMG_UINT32 uiPDumpContextID;            // PDump上下文ID
    IMG_UINT32 ui32PDumpContextIDRefCount;   // PDump引用计数
#endif
    ATOMIC_T sCacheFlags;                   // 缓存标志
    POS_LOCK hLock;                         // 互斥锁
    MMU_Levelx_INFO sBaseLevelInfo;         // 基础级别信息
};

核心特性:
- 连接隔离: 每个连接有独立的MMU上下文
- 原子缓存标志: 高效的缓存失效管理
- PDump支持: 调试和分析工具集成
- 基础级别: 页表层次结构的根节点

工具函数和宏

上下文类型判断宏

#define _MMU_IS_FWKM_CTX(_ctx) ((_ctx)->psConnection == NULL)
#define _MMU_IS_FWKM_CTX_VZGUEST(_ctx) 
    (PVRSRV_VZ_MODE_IS(GUEST, DEVNODE, _ctx->psPhysMemCtx->psDevNode) && _MMU_IS_FWKM_CTX(_ctx))

用途:
- 固件上下文识别: NULL连接表示内核/固件上下文
- 虚拟化支持: 区分客户机固件上下文

地址计算函数

页表索引计算:PC索引计算

static IMG_UINT32 _CalcPCEIdx(IMG_DEV_VIRTADDR sDevVAddr,
                              const MMU_DEVVADDR_CONFIG *psDevVAddrConfig,
                              IMG_BOOL bRoundUp)
{
    IMG_DEV_VIRTADDR sTmpDevVAddr = sDevVAddr;
    if (bRoundUp) {
        sTmpDevVAddr.uiAddr--;
    }
    IMG_UINT32 ui32RetVal = (IMG_UINT32)((sTmpDevVAddr.uiAddr & psDevVAddrConfig->uiPCIndexMask)
                                        >> psDevVAddrConfig->uiPCIndexShift);
    if (bRoundUp) {
        ui32RetVal++;
    }
    return ui32RetVal;
}

巧妙设计:
- 向上舍入: 通过先减1再加1实现向上舍入
- 位操作: 高效的索引计算
- 统一接口: PDE和PTE索引计算使用相同模式

虚拟地址分解图

虚拟地址(64位):
┌─────────┬─────────┬─────────┬─────────┬──────────┐
│  保留   │   PC    │   PD    │   PT    │  偏移    │
│         │  索引   │  索引    │  索引   │          │
└─────────┴─────────┴─────────┴─────────┴──────────┘
          ↑                                 ↑
          PC Shift                    Page Offset

示例(4KB页,3级页表):
63      48 47    39 38    30 29    21 20        0
┌────────┬────────┬────────┬────────┬───────────┐
│保留(16)│PC(9)   │PD(9)   │PT(9)    │偏移(21)   │
└────────┴────────┴────────┴────────┴───────────┘

向上取整的技巧

// 假设要计算地址范围 [0x1000, 0x2500] 需要多少个4KB页
// 不向上取整:0x2500 >> 12 = 2 (错误!)
// 向上取整:(0x2500 - 1) >> 12 + 1 = 3 (正确!)

if (bRoundUp) {
    sTmpDevVAddr.uiAddr--;  // 0x2500 - 1 = 0x24FF
}
ui32RetVal = (sTmpDevVAddr.uiAddr & mask) >> shift;  // 2
if (bRoundUp) {
    ui32RetVal++;  // 3
}

奇偶校验位计算

static inline IMG_UINT64 _GetParityBit(IMG_UINT64 uiSrc)
{
    uiSrc ^= uiSrc >> 32U;
    uiSrc ^= uiSrc >> 16U;
    uiSrc ^= uiSrc >> 8U;
    uiSrc ^= uiSrc >> 4U;
    uiSrc ^= uiSrc >> 2U;
    uiSrc ^= uiSrc >> 1U;
    return (uiSrc & 1U);
}

算法分析:
- 异或折叠: 经典的奇偶校验计算
- 硬件友好: 可以高效地在硬件中实现
- 错误检测: 用于检测页表项传输错误

算法原理

输入:0b11010110 (0xD6)

步骤1: 0b11010110 ^ 0b00000000 = 0b11010110
步骤2: 0b11010110 ^ 0b00001101 = 0b11011011
步骤3: 0b11011011 ^ 0b00000110 = 0b11011101
步骤4: 0b11011101 ^ 0b00000111 = 0b11011010
步骤5: 0b11011010 ^ 0b00000110 = 0b11011100
步骤6: 0b11011100 ^ 0b00000111 = 0b11011011
步骤7: 0b11011011 ^ 0b00000101 = 0b11011110

结果:0b11011110 & 1 = 0

实际上,这个算法计算的是64位数中1的个数的奇偶性

物理内存管理核心函数详解

RA导入分配函数

页表内存分配

static PVRSRV_ERROR _MMU_PhysMem_RAImportAlloc(RA_PERARENA_HANDLE hArenaHandle,
                                               RA_LENGTH_T uiSize,
                                               RA_FLAGS_T uiFlags,
                                               RA_LENGTH_T uBaseAlignment,
                                               const IMG_CHAR *pszAnnotation,
                                               RA_IMPORT *psImport)
{
    MMU_PHYSMEM_CONTEXT *psPhysMemCtx = (MMU_PHYSMEM_CONTEXT *)hArenaHandle;
    PVRSRV_DEVICE_NODE *psDevNode = psPhysMemCtx->psDevNode;
    MMU_MEMORY_MAPPING *psMapping;
    PVRSRV_ERROR eError;
    IMG_UINT32 uiPid = 0;

    psMapping = OSAllocMem(sizeof(MMU_MEMORY_MAPPING));
    PVR_GOTO_IF_NOMEM(psMapping, eError, e0);

    // 分配物理页面
#if defined(SUPPORT_GPUVIRT_VALIDATION)
    psMapping->sMemHandle.uiOSid = psPhysMemCtx->ui32OSid;
    eError = PhysHeapPagesAllocGPV(...);  // GPU虚拟化版本
#else
    // 实际的物理内存分配
    eError = PhysHeapPagesAlloc(psDevNode->psMMUPhysHeap,
                                TRUNCATE_64BITS_TO_SIZE_T(uiSize),
                                &psMapping->sMemHandle,
                                &psMapping->sDevPAddr,
                                uiPid);

    psImport->hPriv = (RA_PERISPAN_HANDLE) psMapping;
    psImport->base = (RA_BASE_T)psMapping->sDevPAddr.uiAddr;
    psImport->uSize = uiSize;

    return PVRSRV_OK;
}

关键机制: 为RA分配器导入物理内存
- RA集成: 与资源分配器紧密集成
- 句柄封装: 将MMU_MEMORY_MAPPING作为私有数据
- 地址转换: 物理地址作为RA基地址

流程图

RA导入释放函数

页表内存释放

static void _MMU_PhysMem_RAImportFree(RA_PERARENA_HANDLE hArenaHandle,
                                      RA_BASE_T uiBase,
                                      RA_PERISPAN_HANDLE hPriv)
{
    MMU_MEMORY_MAPPING *psMapping = (MMU_MEMORY_MAPPING *)hPriv;
    MMU_PHYSMEM_CONTEXT *psPhysMemCtx = (MMU_PHYSMEM_CONTEXT *)hArenaHandle;

    PVR_ASSERT(psMapping->uiCpuVAddrRefCount == 0);

    // 添加到延迟释放列表而不是立即释放
    psMapping->psContext = NULL;
    dllist_add_to_tail(&psPhysMemCtx->sTmpMMUMappingHead, &psMapping->sMMUMappingItem);
}

延迟释放策略:
- 引用计数检查: 确保没有CPU映射
- 延迟队列: 加入临时列表而非立即释放
- 上下文清空: 防止后续访问

注意:不立即释放,而是加入临时列表,等待MMU cache失效

延时释放函数

延时释放机制

static void _SetupCleanup_FreeMMUMapping(MMU_PHYSMEM_CONTEXT *psPhysMemCtx)
{
    MMU_CLEANUP_ITEM *psCleanupItem;
    MMU_CTX_CLEANUP_DATA *psCleanupData = psPhysMemCtx->psCleanupData;
    PVRSRV_DEVICE_NODE *psDevNode = psPhysMemCtx->psDevNode;

    // 检查是否有需要释放的项
    if (dllist_is_empty(&psPhysMemCtx->sTmpMMUMappingHead)) {
        goto e0;
    }
    ...
    // 1. 分配清理项
    psCleanupItem = OSAllocMem(sizeof(*psCleanupItem));

    // 2. 设置同步信息
    psCleanupItem->psSync = NULL;  // 稍后由清理线程设置
    psCleanupItem->uiRequiredSyncVal = psDevNode->ui32NextMMUInvalidateUpdate;
    psCleanupItem->uiRequiredPowerOffCounter = psDevNode->uiPowerOffCounterNext;

    // 3. 移动映射列表
    dllist_replace_head(&psPhysMemCtx->sTmpMMUMappingHead,
                        &psCleanupItem->sMMUMappingHead);

    // 4. 增加引用计数
    OSAtomicIncrement(&psCleanupData->iRef);

    // 5. 添加到清理线程
    PVRSRVCleanupThreadAddWork(psDevNode, &psCleanupItem->sCleanupThreadFn);
    return;
}

工作流程

清理线程函数

static PVRSRV_ERROR _CleanupThread_FreeMMUMapping(void* pvData)
{
    MMU_CLEANUP_ITEM *psCleanup = (MMU_CLEANUP_ITEM *)pvData;
    MMU_CTX_CLEANUP_DATA *psMMUCtxCleanupData = psCleanup->psMMUCtxCleanupData;
    PVRSRV_DEVICE_NODE *psDevNode = psCleanup->psDevNode;
    IMG_BOOL bFreeNow;

    // 第一次调用时psSync为NULL
    if (psCleanup->psSync == NULL)
    {
        IMG_UINT32 uiSyncVal;
        // 请求MMU cache失效
        eError = psDevNode->pfnMMUCacheInvalidateKick(psDevNode, &uiSyncVal);
        if (eError != PVRSRV_OK) {
            return PVRSRV_ERROR_RETRY;  // 失败则重试
        }
        psCleanup->psSync = psDevNode->psMMUCacheSyncPrim;
    }

    // 检查同步值是否满足
    bFreeNow = PVRSRVHasCounter32Advanced(
        OSReadDeviceMem32(psCleanup->psSync->pui32LinAddr),
        psCleanup->uiRequiredSyncVal);

    // 检查超时(500ms)
    if (!bFreeNow) {
        IMG_UINT32 uiTimeStart = psCleanup->sCleanupThreadFn.ui32TimeStart;
        IMG_UINT32 uiTimeEnd = psCleanup->sCleanupThreadFn.ui32TimeEnd;

        if ((uiTimeEnd - uiTimeStart - 500) <= (OSClockms() - uiTimeStart)) {
            bFreeNow = IMG_TRUE;  // 超时,强制释放
        }
    }
}

状态机

物理内存分配

static PVRSRV_ERROR _MMU_PhysMemAlloc(MMU_PHYSMEM_CONTEXT *psPhysMemCtx,
                                      MMU_MEMORY_DESC *psMemDesc,
                                      size_t uiBytes,
                                      size_t uiAlignment)
{
    RA_BASE_T uiPhysAddr;
    PVR_RETURN_IF_INVALID_PARAM(psMemDesc);
    PVR_RETURN_IF_INVALID_PARAM(!psMemDesc->bValid);

    // 从RA分配内存
    eError = RA_Alloc(psPhysMemCtx->psPhysMemRA,
                      uiBytes,
                      RA_NO_IMPORT_MULTIPLIER,
                      0,                    // flags
                      uiAlignment,
                      "",
                      &uiPhysAddr,
                      NULL,
                      (RA_PERISPAN_HANDLE *)&psMemDesc->psMapping);

    // 填充描述符
    psMemDesc->bValid = IMG_TRUE;
    psMemDesc->pvCpuVAddr = NULL;
    psMemDesc->sDevPAddr.uiAddr = (IMG_UINT64)uiPhysAddr;

    // 首次映射到CPU地址空间
    if (psMemDesc->psMapping->uiCpuVAddrRefCount == 0)
    {
        eError = PhysHeapPagesMap(
            psPhysMemCtx->psDevNode->psMMUPhysHeap,
            &psMemDesc->psMapping->sMemHandle,
            psMemDesc->psMapping->uiSize,
            &psMemDesc->psMapping->sDevPAddr,
            &psMemDesc->psMapping->pvCpuVAddr);
    }

    // 计算偏移和虚拟地址
    psMemDesc->psMapping->uiCpuVAddrRefCount++;
    psMemDesc->uiOffset = (psMemDesc->sDevPAddr.uiAddr - 
                          psMemDesc->psMapping->sDevPAddr.uiAddr);
    psMemDesc->pvCpuVAddr = (IMG_UINT8 *)psMemDesc->psMapping->pvCpuVAddr + 
                           psMemDesc->uiOffset;

    return PVRSRV_OK;
}

内存管理特性:
- RA分配: 使用资源分配器获取物理地址
- 懒惰CPU映射: 只在需要时创建CPU映射
- 引用计数: 支持同一物理内存的多个描述符
- 偏移计算: 支持子分配

内存分配流程

内存映射关系

物理内存块:
┌─────────────────────────────────┐
│  MMU_MEMORY_MAPPING             │
│  sDevPAddr = 0x1000             │
│  uiSize = 16KB                  │
│  pvCpuVAddr = 0xFFFF8000        │
└─────────────────────────────────┘

映射到多个描述符:
┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│ MMU_MEMORY_  │     │ MMU_MEMORY_  │     │ MMU_MEMORY_  │
│ DESC 1       │     │ DESC 2       │     │ DESC 3       │
├──────────────┤     ├──────────────┤     ├──────────────┤
│ sDevPAddr:   │     │ sDevPAddr:   │     │ sDevPAddr:   │
│ 0x1000       │     │ 0x2000       │     │ 0x3000       │
│              │     │              │     │              │
│ uiOffset:    │     │ uiOffset:    │     │ uiOffset:    │
│ 0            │     │ 0x1000       │     │ 0x2000       │
│              │     │              │     │              │
│ pvCpuVAddr:  │     │ pvCpuVAddr:  │     │ pvCpuVAddr:  │
│ 0xFFFF8000   │     │ 0xFFFF9000   │     │ 0xFFFFA000   │
└──────────────┘     └──────────────┘     └──────────────┘

整体调用路径

_MMU_AllocLevel()
   └── _PxMemAlloc()
         └── _MMU_PhysMemAlloc()
               └── RA_Alloc()
               └── PhysHeapPagesMap()

行为分析

eError = RA_Alloc(psPhysMemCtx->psPhysMemRA,
                  uiBytes, RA_NO_IMPORT_MULTIPLIER, 0, uiAlignment, "",
                  &uiPhysAddr, NULL, (RA_PERISPAN_HANDLE *)&psMemDesc->psMapping);
  • 这里调用 Resource Allocator (RA) 管理的物理内存池。

  • uiBytes 就是 _PxMemAlloc 传下来的大小(即页表大小,经过 PVR_ALIGN 对齐)。

  • RA_Alloc 通常会返回连续的“设备物理地址”空间(不一定是 CPU 物理连续的)。

if (psMemDesc->psMapping->uiCpuVAddrRefCount == 0)
{
    eError = PhysHeapPagesMap(..., &psMemDesc->psMapping->sMemHandle, ...);
}
  • 第一次分配时会走 PhysHeapPagesMap(),进行 CPU 端映射;

  • 返回的 pvCpuVAddr 通常通过 ioremap_wc 建立;

  • 如果 RA 返回的物理区块是分散的(多个 span),PhysHeapPagesMap 可能循环调用 ioremap_page_range() 或多次 ioremap();

  • 每个 mapping 都被记录在 psMapping 里以便复用。

这说明:
- 用的 PhysHeap(通常是 GPU MMU 的页表专用 heap)内部管理的是页粒度资源 (4K);

  • RA 层将多个 4K 物理页拼成一个逻辑连续区;

  • CPU 映射层 (PhysHeapPagesMap) 则循环 ioremap 每页来建立映射。
    也就是说:

  1. 只要 RA 分配得到的 psMapping 是新的(第一次引用),就会调用一次 PhysHeapPagesMap()。

  2. PhysHeapPagesMap() 通常会调用 ioremap()(或者 vmap()),把物理页映射成 CPU 可访问的虚拟地址。

  3. 后续如果同一个 mapping 被重复引用,不会再次 ioremap(),只增加引用计数。

但问题在于:

  • 大部分 _MMU_PhysMemAlloc() 的调用都会通过 RA_Alloc() 得到一个新的 mapping 结构,
  • 所以几乎每次都会触发一次 ioremap()。

性能瓶颈点
- 每次 RA_Alloc 都返回新的 mapping → 导致每次 ioremap()

  • 每个页表页通常 4K → 创建 1MB 映射就可能触发 256 次 ioremap()

  • ioremap() 涉及:

    • 分配新的 vm_struct

    • 修改内核页表(set_pte)

    • cache/TLB 同步

    • 这就是页表初始化阶段非常耗时的根源。

标志位转换

static INLINE PVRSRV_ERROR _MMU_ConvertDevMemFlags(
    IMG_BOOL bInvalidate,
    PVRSRV_MEMALLOCFLAGS_T uiMappingFlags,
    MMU_PROTFLAGS_T *puiMMUProtFlags,
MMU_CONTEXT *psMMUContext)
{
    PVRSRV_ERROR eError = PVRSRV_OK;
    IMG_UINT32 uiGPUCacheMode;
    PVRSRV_DEVICE_NODE *psDevNode = psMMUContext->psPhysMemCtx->psDevNode;
    /* 如果是无效映射,直接返回无效标志 */
    if (bInvalidate == IMG_TRUE)
    {
        *puiMMUProtFlags |= MMU_PROTFLAGS_INVALID;
        return eError;
    }

    /* 转换为有效映射的标志 */
    *puiMMUProtFlags |= ((uiMappingFlags & PVRSRV_MEMALLOCFLAG_DEVICE_FLAGS_MASK)
            >> PVRSRV_MEMALLOCFLAG_DEVICE_FLAGS_OFFSET)
            << MMU_PROTFLAGS_DEVICE_OFFSET;

    /* 可读标志 */
    if (PVRSRV_CHECK_GPU_READABLE(uiMappingFlags))
    {
        *puiMMUProtFlags |= MMU_PROTFLAGS_READABLE;
    }

    /* 可写标志 */
    if (PVRSRV_CHECK_GPU_WRITEABLE(uiMappingFlags))
    {
        *puiMMUProtFlags |= MMU_PROTFLAGS_WRITEABLE;
    }

    /* 缓存模式转换 */
    eError = DevmemDeviceCacheMode(uiMappingFlags, &uiGPUCacheMode);
    PVR_RETURN_IF_ERROR(eError);

    switch (uiGPUCacheMode)
    {
        case PVRSRV_MEMALLOCFLAG_GPU_UNCACHED:
        case PVRSRV_MEMALLOCFLAG_GPU_UNCACHED_WC:
            break;  /* 不缓存,不设置任何标志 */
        case PVRSRV_MEMALLOCFLAG_GPU_CACHED:
            *puiMMUProtFlags |= MMU_PROTFLAGS_CACHED;
            break;
        default:
            return PVRSRV_ERROR_INVALID_PARAMS;
    }

    /* 缓存一致性 */
    if (DevmemDeviceCacheCoherency(psDevNode, uiMappingFlags))
    {
        *puiMMUProtFlags |= MMU_PROTFLAGS_CACHE_COHERENT;
    }

    /* 设备特定的保护标志调整 */
    if (psDevNode->pfnMMUTweakProtFlags)
    {
        psDevNode->pfnMMUTweakProtFlags(psDevNode, psMMUContext->psDevAttrs, 
                                       uiMappingFlags, puiMMUProtFlags);
    }

    return PVRSRV_OK;
}

标志位映射图

PVRSRV_MEMALLOCFLAGS_T (应用层标志)
┌─────────────────────────────────────────┐
│ GPU_READABLE    │ GPU_WRITEABLE │ ...   │
│ GPU_CACHED      │ COHERENT      │ ...   │
└─────────────────────────────────────────┘
         │                  │
         ↓                  ↓
MMU_PROTFLAGS_T (MMU层标志)
┌─────────────────────────────────────────┐
│ READABLE       │ WRITEABLE     │ ...    │
│ CACHED         │ CACHE_COHERENT│ ...    │
└─────────────────────────────────────────┘
         │                  │
         ↓                  ↓
硬件PTE标志 (硬件特定)
┌─────────────────────────────────────────┐
│ Valid │ Read │ Write │ Cache │ Parity  │
└─────────────────────────────────────────┘

页表内存分配和初始化:_PxMemAlloc

static PVRSRV_ERROR _PxMemAlloc(
    MMU_CONTEXT *psMMUContext,
    IMG_UINT32 uiNumEntries,
    const MMU_PxE_CONFIG *psConfig,
    MMU_LEVEL eMMULevel,
    MMU_MEMORY_DESC *psMemDesc,
#if defined(PVRSRV_MMU_PARITY_ON_PTALLOC_AND_PTEUNMAP)
    IMG_DEV_VIRTADDR* psRunningDevVAddr,
    IMG_UINT32 uiLog2DataPageSize,
#endif
    IMG_UINT32 uiLog2Align)
{
    PVRSRV_ERROR eError;
    size_t uiBytes;
    size_t uiAlign;
    PVRSRV_DEVICE_NODE *psDevNode = psMMUContext->psPhysMemCtx->psDevNode;

    PVR_ASSERT(psConfig->uiBytesPerEntry != 0);

    /* 计算需要的字节数 */
    uiBytes = uiNumEntries * psConfig->uiBytesPerEntry;
    uiAlign = 1 << uiLog2Align;

    /* 
     * 如果硬件要求对齐,需要将内存扩展到对齐边界
     * 这是因为未初始化的数据可能被MMU当作有效的PTE读取
     */
    uiBytes = PVR_ALIGN(uiBytes, uiAlign);
    /* 分配物理内存 */
    eError = _MMU_PhysMemAlloc(psMMUContext->psPhysMemCtx,
                               psMemDesc, uiBytes, uiAlign);
    if (eError != PVRSRV_OK)
    {
        PVR_LOG_GOTO_WITH_ERROR("_MMU_PhysMemAlloc", eError, 
                               PVRSRV_ERROR_OUT_OF_MEMORY, e0);
    }

#if defined(PVRSRV_MMU_PARITY_ON_PTALLOC_AND_PTEUNMAP)
    /* 如果需要奇偶校验位,使用预计算的模式 */
    if (psConfig->uiParityBitMask && eMMULevel == MMU_LEVEL_1 && 
        psRunningDevVAddr != NULL)
    {
        IMG_UINT32 uiParity = (IMG_UINT32)_GetParityBit(
            psRunningDevVAddr->uiAddr ^ 0ULL);

        /* 使用预计算的奇偶校验模式 */
        OSCachedMemCopy(psMemDesc->pvCpuVAddr, 
            psMMUContext->psDevAttrs->pui64PrecomputedAllocParity[uiParity], 
            uiBytes);

        /* 更新运行地址 */
        psRunningDevVAddr->uiAddr += (1 << uiLog2DataPageSize) * uiNumEntries;
    }
    else
#endif
    {
        /* 清零页表 */
        OSCachedMemSet(psMemDesc->pvCpuVAddr, 0, uiBytes);
    }

    /* 刷新CPU缓存 */
    eError = PhysHeapPagesClean(psDevNode->psMMUPhysHeap,
                                &psMemDesc->psMapping->sMemHandle,
                                psMemDesc->uiOffset,
                                psMemDesc->uiSize);
    PVR_GOTO_IF_ERROR(eError, e1);

#if defined(PDUMP)
    PDUMPCOMMENT(psDevNode, "Alloc MMU object");

    PDumpMMUMalloc(psDevNode,
                   psMMUContext->psDevAttrs->pszMMUPxPDumpMemSpaceName,
                   eMMULevel,
                   &psMemDesc->sDevPAddr,
                   uiBytes,
                   uiAlign,
                   psMMUContext->psDevAttrs->eMMUType);

    PDumpMMUDumpPxEntries(...);  /* PDump所有条目 */
#endif

    return PVRSRV_OK;

e1:
    _MMU_PhysMemFree(psMMUContext->psPhysMemCtx, psMemDesc);
e0:
    return eError;
}

内存对齐的重要性

示例:16字节的PT,但要求64字节对齐

不对齐初始化(危险):
┌────────────┬──────────────────────┐
│ PT (16B)   │ 未初始化数据 (48B)  │
│ 已清零     │ 可能含有垃圾数据     │
└────────────┴──────────────────────┘
              ↑
              MMU可能读取这部分,
              导致错误的pending标志

正确的对齐初始化:
┌────────────┬──────────────────────┐
│ PT (16B)   │ 已清零数据 (48B)    │
│ 已清零     │ 已清零               │
└────────────┴──────────────────────┘
              ↑
              安全,MMU读取到的都是0

设置PTE:_SetupPTE

这是最核心的函数之一,负责设置页表项。

static INLINE PVRSRV_ERROR _SetupPTE(
    MMU_CONTEXT *psMMUContext,
    MMU_Levelx_INFO *psLevel,
    IMG_UINT32 uiIndex,
    const MMU_PxE_CONFIG *psConfig,
    const IMG_DEV_PHYADDR *psDevPAddr,
    const IMG_DEV_VIRTADDR *psDevVAddr,
    IMG_BOOL bUnmap,
#if defined(PDUMP)
    const IMG_CHAR *pszMemspaceName,
    const IMG_CHAR *pszSymbolicAddr,
    IMG_DEVMEM_OFFSET_T uiSymbolicAddrOffset,
#endif
    IMG_UINT64 uiProtFlags)
{
    MMU_MEMORY_DESC *psMemDesc = &psLevel->sMemDesc;
    IMG_UINT64 ui64PxE64;
    IMG_UINT64 uiAddr = psDevPAddr->uiAddr;
    PVRSRV_DEVICE_NODE *psDevNode = psMMUContext->psPhysMemCtx->psDevNode;

#if defined(PVRSRV_MMU_PARITY_ON_PTALLOC_AND_PTEUNMAP)
    IMG_BOOL bParity = psConfig->uiParityBitMask;
#else
    IMG_BOOL bParity = psConfig->uiParityBitMask && !bUnmap;
#endif

PTE格式构造

64位PTE结构(示例):
┌──┬─────────────────────────────────────┬───────────┬──┬──┬──┐
│P │    物理地址 (40位)                  │   保留    │C │W │V │
│  │    [51:12]                          │           │  │  │  │
└──┴─────────────────────────────────────┴───────────┴──┴──┴──┘
 63                                                    3  2  1  0

 P = Parity bit (奇偶校验位)
 C = Cached (缓存位)
 W = Writeable (可写位)
 V = Valid (有效位)

构造PTE的步骤

    /* 设备特定的地址验证/调整 */
    if (psDevNode->pfnValidateOrTweakPhysAddrs)
    {
        PVRSRV_ERROR eErr = psDevNode->pfnValidateOrTweakPhysAddrs(
            psDevNode,
            psMMUContext->psDevAttrs,
            &uiAddr);
        PVR_LOG_RETURN_IF_ERROR(eErr, "_SetupPTE");
    }

    /* 计算PTE值 */
    ui64PxE64 = uiAddr                      /* 物理地址 */
        >> psConfig->uiAddrLog2Align        /* 右移对齐位数 */
        << psConfig->uiAddrShift            /* 左移到正确位置 */
        & psConfig->uiAddrMask;             /* 应用地址掩码 */

    ui64PxE64 |= uiProtFlags;               /* 添加保护标志 */

    /* 添加奇偶校验位 */
    if (bParity)
    {
        ui64PxE64 |= _GetParityBit(psDevVAddr->uiAddr ^ psDevPAddr->uiAddr) 
                     << psConfig->uiParityBitShift;
    }

写入PTE

    /* 根据条目大小写入 */
    if (psConfig->uiBytesPerEntry == 8)
    {
        IMG_UINT64 *pui64Px = psMemDesc->pvCpuVAddr;
#if defined(SUPPORT_VGPU_GUEST)
        XdxUpdatePTE(&pui64Px[uiIndex], ui64PxE64, psDevVAddr->uiAddr);
#else
        pui64Px[uiIndex] = ui64PxE64;
#endif
    }
    else if (psConfig->uiBytesPerEntry == 4)
    {
        IMG_UINT32 *pui32Px = psMemDesc->pvCpuVAddr;
        PVR_ASSERT(ui64PxE64 == (ui64PxE64 & 0xffffffffU));  /* 确保不溢出 */
        pui32Px[uiIndex] = (IMG_UINT32)ui64PxE64;
    }

HTB日志记录

    /* 记录修改 */
    HTBLOGK(HTB_SF_MMU_PAGE_OP_TABLE,
            HTBLOG_PTR_BITS_HIGH(psLevel), HTBLOG_PTR_BITS_LOW(psLevel),
            uiIndex, MMU_LEVEL_1,
            HTBLOG_U64_BITS_HIGH(ui64PxE64), HTBLOG_U64_BITS_LOW(ui64PxE64),
            !bUnmap);

#if defined(PDUMP)
    PDumpMMUDumpPxEntries(...);  /* PDump这个条目 */
#endif

    return PVRSRV_OK;
}

设置PxE(高级页表)

用于设置PC和PD级别的条目。

static void _SetupPxE(
    MMU_CONTEXT *psMMUContext,
    MMU_Levelx_INFO *psLevel,
    IMG_UINT32 uiIndex,
    const MMU_PxE_CONFIG *psConfig,
    MMU_LEVEL eMMULevel,
    const IMG_DEV_PHYADDR *psDevPAddr,
#if defined(PDUMP)
    const IMG_CHAR *pszMemspaceName,
    const IMG_CHAR *pszSymbolicAddr,
    IMG_DEVMEM_OFFSET_T uiSymbolicAddrOffset,
#endif
    MMU_PROTFLAGS_T uiProtFlags,
    IMG_UINT32 uiLog2DataPageSize)
{
    PVRSRV_DEVICE_NODE *psDevNode = psMMUContext->psPhysMemCtx->psDevNode;
    MMU_MEMORY_DESC *psMemDesc = &psLevel->sMemDesc;
    IMG_UINT32 (*pfnDerivePxEProt4)(IMG_UINT32);
    IMG_UINT64 (*pfnDerivePxEProt8)(IMG_UINT32, IMG_UINT32);

    /* 处理无效条目 */
    if (!psDevPAddr)
    {
        if (~uiProtFlags & MMU_PROTFLAGS_INVALID)
        {
            PVR_DPF((PVR_DBG_ERROR, "No physical address but not invalidating"));
            uiProtFlags |= MMU_PROTFLAGS_INVALID;
        }
        psDevPAddr = &gsBadDevPhyAddr;  /* 使用坏地址 */
    }
    else
    {
        if (uiProtFlags & MMU_PROTFLAGS_INVALID)
        {
            PVR_DPF((PVR_DBG_ERROR, "Has physical address but invalidating"));
        }
    }

获取保护标志派生函数

    /* 根据级别选择正确的派生函数 */
    switch (eMMULevel)
    {
        case MMU_LEVEL_3:
            pfnDerivePxEProt4 = psMMUContext->psDevAttrs->pfnDerivePCEProt4;
            pfnDerivePxEProt8 = psMMUContext->psDevAttrs->pfnDerivePCEProt8;
            break;
        case MMU_LEVEL_2:
            pfnDerivePxEProt4 = psMMUContext->psDevAttrs->pfnDerivePDEProt4;
            pfnDerivePxEProt8 = psMMUContext->psDevAttrs->pfnDerivePDEProt8;
            break;
        case MMU_LEVEL_1:
            pfnDerivePxEProt4 = psMMUContext->psDevAttrs->pfnDerivePTEProt4;
            pfnDerivePxEProt8 = psMMUContext->psDevAttrs->pfnDerivePTEProt8;
            break;
        default:
            PVR_ASSERT(0);
            return;
    }

写入PxE

    switch (psConfig->uiBytesPerEntry)
    {
        case 4:
        {
            IMG_UINT32 *pui32Px = psMemDesc->pvCpuVAddr;
            IMG_UINT64 ui64PxE64;

            ui64PxE64 = psDevPAddr->uiAddr
                >> psConfig->uiAddrLog2Align
                << psConfig->uiAddrShift
                & psConfig->uiAddrMask;

            ui64PxE64 |= (IMG_UINT64)pfnDerivePxEProt4(uiProtFlags);

            /* 不应该使无效的条目再次无效 */
            if (uiProtFlags & MMU_PROTFLAGS_INVALID)
            {
                PVR_ASSERT(pui32Px[uiIndex] != ui64PxE64);
            }

            pui32Px[uiIndex] = (IMG_UINT32)ui64PxE64;

            HTBLOGK(HTB_SF_MMU_PAGE_OP_TABLE, ...);
            break;
        }
        case 8:
        {
            IMG_UINT64 *pui64Px = psMemDesc->pvCpuVAddr;
            IMG_UINT64 ui64PxE64;

            ui64PxE64 = psDevPAddr->uiAddr
                >> psConfig->uiAddrLog2Align
                << psConfig->uiAddrShift
                & psConfig->uiAddrMask;

            ui64PxE64 |= pfnDerivePxEProt8(uiProtFlags, uiLog2DataPageSize);

            pui64Px[uiIndex] = ui64PxE64;

            HTBLOGK(HTB_SF_MMU_PAGE_OP_TABLE, ...);
            break;
        }
    }

#if defined(PDUMP)
    PDumpMMUDumpPxEntries(...);
#endif

    /* 缓存失效 */
    psDevNode->pfnMMUCacheInvalidate(psDevNode, psMMUContext,
                                     eMMULevel,
                                     uiProtFlags & MMU_PROTFLAGS_INVALID);
}

MMU层次分配:_MMU_AllocLevel

这是一个递归函数,用于分配多级页表。

static PVRSRV_ERROR _MMU_AllocLevel(
    MMU_CONTEXT *psMMUContext,
    MMU_Levelx_INFO *psLevel,
    IMG_UINT32 auiStartArray[],
    IMG_UINT32 auiEndArray[],
    IMG_UINT32 auiEntriesPerPxArray[],
    const MMU_PxE_CONFIG *apsConfig[],
    MMU_LEVEL aeMMULevel[],
    IMG_UINT32 *pui32CurrentLevel,
    IMG_UINT32 uiStartIndex,
    IMG_UINT32 uiEndIndex,
#if defined(PVRSRV_MMU_PARITY_ON_PTALLOC_AND_PTEUNMAP)
    IMG_DEV_VIRTADDR* psRunningDevVAddr,
    IMG_BOOL bSetParity,
#endif
    IMG_BOOL bFirst,
    IMG_BOOL bLast,
    IMG_UINT32 uiLog2DataPageSize)
{
    IMG_UINT32 uiThisLevel = *pui32CurrentLevel;
    const MMU_PxE_CONFIG *psConfig = apsConfig[uiThisLevel];
    PVRSRV_ERROR eError = PVRSRV_ERROR_OUT_OF_MEMORY;
    IMG_UINT32 uiAllocState = 99;
    IMG_UINT32 i;
    PVRSRV_DEVICE_NODE *psDevNode = psMMUContext->psPhysMemCtx->psDevNode;

递归分配流程图

核心循环

    for (i = uiStartIndex; i < uiEndIndex; i++)
    {
        /* 只在非最后一级分配 */
        if (aeMMULevel[uiThisLevel] != MMU_LEVEL_1)
        {
            IMG_UINT32 uiNextStartIndex;
            IMG_UINT32 uiNextEndIndex;
            IMG_BOOL bNextFirst;
            IMG_BOOL bNextLast;

            /* 如果下一级不存在,分配它 */
            if (!psLevel->apsNextLevel[i])
            {
                MMU_Levelx_INFO *psNextLevel;
                IMG_UINT32 ui32AllocSize;
                IMG_UINT32 uiNextEntries;

                /* 分配下一级结构 */
                uiNextEntries = auiEntriesPerPxArray[uiThisLevel + 1];
                ui32AllocSize = sizeof(MMU_Levelx_INFO);
                if (aeMMULevel[uiThisLevel + 1] != MMU_LEVEL_1)
                {
                    ui32AllocSize += IMG_FLEX_ARRAY_SIZE(
                        sizeof(MMU_Levelx_INFO *), uiNextEntries);
                }
                psNextLevel = OSAllocZMem(ui32AllocSize);
                PVR_GOTO_IF_NOMEM(psNextLevel, eError, e0);

                /* 关联到本级 */
                psLevel->apsNextLevel[i] = psNextLevel;

                psNextLevel->ui32NumOfEntries = uiNextEntries;
                psNextLevel->ui32RefCount = 0;

                /* 分配物理内存 */
                eError = _PxMemAlloc(psMMUContext, uiNextEntries,
                                     apsConfig[uiThisLevel + 1],
                                     aeMMULevel[uiThisLevel + 1],
                                     &psNextLevel->sMemDesc,
#if defined(PVRSRV_MMU_PARITY_ON_PTALLOC_AND_PTEUNMAP)
                                     psRunningDevVAddr,
                                     uiLog2DataPageSize,
#endif
                                     psConfig->uiAddrLog2Align);
                PVR_GOTO_IF_ERROR(eError, e0);

                /* 连接PxE */
                _SetupPxE(psMMUContext,
                         psLevel,
                         i,
                         psConfig,
                         aeMMULevel[uiThisLevel],
                         &psNextLevel->sMemDesc.sDevPAddr,
#if defined(PDUMP)
                         NULL, NULL, 0,
#endif
                         0,
                         uiLog2DataPageSize);

                psLevel->ui32RefCount++;
            }

索引范围计算

            /* 计算下一级的索引范围 */
            if (bFirst && (i == uiStartIndex))
            {
                uiNextStartIndex = auiStartArray[uiThisLevel + 1];
                bNextFirst = IMG_TRUE;
            }
            else
            {
                uiNextStartIndex = 0;
                bNextFirst = IMG_FALSE;
            }

            if (bLast && (i == (uiEndIndex - 1)))
            {
                uiNextEndIndex = auiEndArray[uiThisLevel + 1];
                bNextLast = IMG_TRUE;
            }
            else
            {
                uiNextEndIndex = auiEntriesPerPxArray[uiThisLevel + 1];
                bNextLast = IMG_FALSE;
            }

索引范围示例

假设分配虚拟地址范围:0x1000 - 0x5FFF (3个4KB页)

Level 2 (PD):
索引0: [0x0000-0x1FFF]
索引1: [0x2000-0x3FFF]  ← 我们的范围
索引2: [0x4000-0x5FFF]  ← 我们的范围
索引3: [0x6000-0x7FFF]

对于索引1(bFirst=TRUE):
  - uiNextStartIndex = auiStartArray[Level1] = 1 (从PT的第1项开始)
  - bNextFirst = TRUE

对于索引2(bLast=TRUE):
  - uiNextEndIndex = auiEndArray[Level1] = 512 (到PT的最后)
  - bNextLast = TRUE

递归调用和错误处理

            /* 递归到下一级 */
            (*pui32CurrentLevel)++;
            eError = _MMU_AllocLevel(psMMUContext, psLevel->apsNextLevel[i],
                                     auiStartArray,
                                     auiEndArray,
                                     auiEntriesPerPxArray,
                                     apsConfig,
                                     aeMMULevel,
                                     pui32CurrentLevel,
                                     uiNextStartIndex,
                                     uiNextEndIndex,
#if defined(PVRSRV_MMU_PARITY_ON_PTALLOC_AND_PTEUNMAP)
                                     psRunningDevVAddr,
                                     bSetParity,
#endif
                                     bNextFirst,
                                     bNextLast,
                                     uiLog2DataPageSize);
            (*pui32CurrentLevel)--;
            if (eError != PVRSRV_OK)
            {
                uiAllocState = 3;
                goto e0;
            }
        }
        else
        {
            /* Level 1,只增加引用计数 */
            psLevel->ui32RefCount++;
        }

        /* 检查引用计数溢出 */
        if (psLevel->ui32RefCount > psLevel->ui32NumOfEntries)
        {
            uiAllocState = 4;
            PVR_GOTO_WITH_ERROR(eError, PVRSRV_ERROR_BAD_MAPPING, e0);
        }
    }

刷新缓存

    /* Level 1在写入PTE时刷新,这里只刷新高级 */
    if (aeMMULevel[uiThisLevel] != MMU_LEVEL_1)
    {
        eError = PhysHeapPagesClean(psDevNode->psMMUPhysHeap,
                                    &psLevel->sMemDesc.psMapping->sMemHandle,
                                    uiStartIndex * psConfig->uiBytesPerEntry + 
                                    psLevel->sMemDesc.uiOffset,
                                    (uiEndIndex - uiStartIndex) * 
                                    psConfig->uiBytesPerEntry);
        PVR_LOG_IF_ERROR(eError, "PhysHeapPagesClean");
    }

    return PVRSRV_OK;

错误回滚

e0:
    /* 处理当前索引i */
    if (aeMMULevel[uiThisLevel] != MMU_LEVEL_1)
    {
        if (uiAllocState >= 3)
        {
            if (psLevel->apsNextLevel[i] != NULL &&
                psLevel->apsNextLevel[i]->ui32RefCount == 0)
            {
                psLevel->ui32RefCount--;
            }
        }
        if (uiAllocState >= 2)
        {
            if (psLevel->apsNextLevel[i] != NULL &&
            psLevel->apsNextLevel[i]->ui32RefCount == 0)
            {
                _PxMemFree(psMMUContext, &psLevel->apsNextLevel[i]->sMemDesc,
                aeMMULevel[uiThisLevel + 1]);
            }
        }
        if (psLevel->apsNextLevel[i] != NULL &&
            psLevel->apsNextLevel[i]->ui32RefCount == 0)
        {
            OSFreeMem(psLevel->apsNextLevel[i]);
            psLevel->apsNextLevel[i] = NULL;
        }
    }
    else
    {
        psLevel->ui32RefCount--;
    }
e1:
    i--;  /* 回滚前面已分配的索引 */

    /* 反向遍历,释放已分配的内容 */
    for (/* i already set */; i >= uiStartIndex && i < uiEndIndex; i--)
    {
        if (aeMMULevel[uiThisLevel] != MMU_LEVEL_1)
        {
            IMG_UINT32 uiNextStartIndex;
            IMG_UINT32 uiNextEndIndex;
            IMG_BOOL bNextFirst;
            IMG_BOOL bNextLast;

            /* 重新计算索引范围 */
            if (bFirst && (i == uiStartIndex))
            {
                uiNextStartIndex = auiStartArray[uiThisLevel + 1];
                bNextFirst = IMG_TRUE;
            }
            else
            {
                uiNextStartIndex = 0;
                bNextFirst = IMG_FALSE;
            }

            if (bLast && (i == (uiEndIndex - 1)))
            {
                uiNextEndIndex = auiEndArray[uiThisLevel + 1];
                bNextLast = IMG_TRUE;
            }
            else
            {
                uiNextEndIndex = auiEntriesPerPxArray[uiThisLevel + 1];
                bNextLast = IMG_FALSE;
            }

            /* 递归释放 */
            (*pui32CurrentLevel)++;
            if (_MMU_FreeLevel(psMMUContext, psLevel->apsNextLevel[i],
                               auiStartArray, auiEndArray,
                               auiEntriesPerPxArray, apsConfig,
                               aeMMULevel, pui32CurrentLevel,
                               uiNextStartIndex, uiNextEndIndex,
                               bNextFirst, bNextLast, uiLog2DataPageSize))
            {
                _PxMemFree(psMMUContext, &psLevel->apsNextLevel[i]->sMemDesc,
                           aeMMULevel[uiThisLevel + 1]);
                OSFreeMem(psLevel->apsNextLevel[i]);
                psLevel->apsNextLevel[i] = NULL;

                psLevel->ui32RefCount--;
                PVR_ASSERT(psLevel->ui32RefCount <= psLevel->ui32NumOfEntries);
            }
            (*pui32CurrentLevel)--;
        }
        else
        {
            psLevel->ui32RefCount--;
            PVR_ASSERT(psLevel->ui32RefCount <= psLevel->ui32NumOfEntries);
        }
    }

    return eError;
}

MMU层次释放:_MMU_FreeLevel

递归释放函数,与分配函数配对。

static IMG_BOOL _MMU_FreeLevel(
    MMU_CONTEXT *psMMUContext,
    MMU_Levelx_INFO *psLevel,
    IMG_UINT32 auiStartArray[],
    IMG_UINT32 auiEndArray[],
    IMG_UINT32 auiEntriesPerPxArray[],
    const MMU_PxE_CONFIG *apsConfig[],
    MMU_LEVEL aeMMULevel[],
    IMG_UINT32 *pui32CurrentLevel,
    IMG_UINT32 uiStartIndex,
    IMG_UINT32 uiEndIndex,
    IMG_BOOL bFirst,
    IMG_BOOL bLast,
    IMG_UINT32 uiLog2DataPageSize)
{
    IMG_UINT32 uiThisLevel = *pui32CurrentLevel;
    const MMU_PxE_CONFIG *psConfig = apsConfig[uiThisLevel];
    IMG_UINT32 i;
    IMG_BOOL bFreed = IMG_FALSE;
    PVRSRV_DEVICE_NODE *psDevNode = psMMUContext->psPhysMemCtx->psDevNode;

    MMU_OBJ_DBG((PVR_DBG_ERROR,
                 "_MMU_FreeLevel: level = %u, range %u - %u, refcount = %u",
                 aeMMULevel[uiThisLevel], uiStartIndex, uiEndIndex,
                 (psLevel != NULL ? psLevel->ui32RefCount : IMG_UINT32_MAX)));

    /* 参数检查 */
    PVR_ASSERT(*pui32CurrentLevel < MMU_MAX_LEVEL);

    if (psLevel == NULL)
    {
        PVR_DPF((PVR_DBG_ERROR, "%s: invalid MMU level data", __func__));
        goto ErrReturn;
    }

    for (i = uiStartIndex; i < uiEndIndex; i++)
    {
        if (aeMMULevel[uiThisLevel] != MMU_LEVEL_1)
        {
            MMU_Levelx_INFO *psNextLevel = psLevel->apsNextLevel[i];
            IMG_UINT32 uiNextStartIndex;
            IMG_UINT32 uiNextEndIndex;
            IMG_BOOL bNextFirst;
            IMG_BOOL bNextLast;

            /* 计算下一级范围 */
            if (bFirst && (i == uiStartIndex))
            {
                uiNextStartIndex = auiStartArray[uiThisLevel + 1];
                bNextFirst = IMG_TRUE;
            }
            else
            {
                uiNextStartIndex = 0;
                bNextFirst = IMG_FALSE;
            }

            if (bLast && (i == (uiEndIndex - 1)))
            {
                uiNextEndIndex = auiEndArray[uiThisLevel + 1];
                bNextLast = IMG_TRUE;
            }
            else
            {
                uiNextEndIndex = auiEntriesPerPxArray[uiThisLevel + 1];
                bNextLast = IMG_FALSE;
            }

            /* 递归释放下一级 */
            (*pui32CurrentLevel)++;
            if (_MMU_FreeLevel(psMMUContext, psNextLevel, auiStartArray,
                               auiEndArray, auiEntriesPerPxArray,
                               apsConfig, aeMMULevel, pui32CurrentLevel,
                               uiNextStartIndex, uiNextEndIndex,
                               bNextFirst, bNextLast, uiLog2DataPageSize))
            {
                /* 下一级被完全释放,断开连接 */
                _SetupPxE(psMMUContext,
                          psLevel,
                          i,
                          psConfig,
                          aeMMULevel[uiThisLevel],
                          NULL,  /* 无效地址 */
#if defined(PDUMP)
                          NULL, NULL, 0,
#endif
                          MMU_PROTFLAGS_INVALID,
                          uiLog2DataPageSize);

                /* 释放下一级的内存 */
                _PxMemFree(psMMUContext, &psNextLevel->sMemDesc, 
                           aeMMULevel[*pui32CurrentLevel]);
                OSFreeMem(psNextLevel);

                /* 减少引用计数 */
                psLevel->ui32RefCount--;
                psLevel->apsNextLevel[i] = NULL;

                PVR_ASSERT(psLevel->ui32RefCount <= psLevel->ui32NumOfEntries);
            }
            (*pui32CurrentLevel)--;
        }
        else
        {
            /* Level 1,直接减少引用计数 */
            psLevel->ui32RefCount--;
        }

        /* 如果引用计数为0且不是基础级别,标记为可释放 */
        if ((psLevel->ui32RefCount == 0) && 
            (psLevel != &psMMUContext->sBaseLevelInfo))
        {
            bFreed = IMG_TRUE;
        }
    }

    /* 刷新缓存 */
    if (aeMMULevel[uiThisLevel] != MMU_LEVEL_1)
    {
        PVRSRV_ERROR eError;
        eError = PhysHeapPagesClean(psDevNode->psMMUPhysHeap,
                                    &psLevel->sMemDesc.psMapping->sMemHandle,
                                    uiStartIndex * psConfig->uiBytesPerEntry + 
                                    psLevel->sMemDesc.uiOffset,
                                    (uiEndIndex - uiStartIndex) * 
                                    psConfig->uiBytesPerEntry);
        PVR_LOG_IF_ERROR(eError, "PhysHeapPagesClean");
    }

ErrReturn:
    MMU_OBJ_DBG((PVR_DBG_ERROR, "_MMU_FreeLevel end: level = %u, refcount = %u",
                 aeMMULevel[uiThisLevel],
                 bFreed ? 0 : (psLevel != NULL ? psLevel->ui32RefCount : IMG_UINT32_MAX)));

    return bFreed;
}

获取级别数据:_MMU_GetLevelData

static void _MMU_GetLevelData(
    MMU_CONTEXT *psMMUContext,
    IMG_DEV_VIRTADDR sDevVAddrStart,
    IMG_DEV_VIRTADDR sDevVAddrEnd,
    IMG_UINT32 uiLog2DataPageSize,
    IMG_UINT32 auiStartArray[],
    IMG_UINT32 auiEndArray[],
    IMG_UINT32 auiEntriesPerPx[],
    const MMU_PxE_CONFIG *apsConfig[],
    MMU_LEVEL aeMMULevel[],
    const MMU_DEVVADDR_CONFIG **ppsMMUDevVAddrConfig,
    IMG_HANDLE *phPriv)
{
    const MMU_PxE_CONFIG *psMMUPDEConfig;
    const MMU_PxE_CONFIG *psMMUPTEConfig;
    const MMU_DEVVADDR_CONFIG *psDevVAddrConfig;
    MMU_DEVICEATTRIBS *psDevAttrs = psMMUContext->psDevAttrs;
    PVRSRV_ERROR eError;
    IMG_UINT32 i = 0;

    /* 获取页大小配置 */
    eError = psDevAttrs->pfnGetPageSizeConfiguration(uiLog2DataPageSize,
                                                     &psMMUPDEConfig,
                                                     &psMMUPTEConfig,
                                                     ppsMMUDevVAddrConfig,
                                                     phPriv);
    PVR_LOG_RETURN_VOID_IF_ERROR(eError, "GetPageSizeConfiguration");

    psDevVAddrConfig = *ppsMMUDevVAddrConfig;

填充级别数据

    /* Level 3 (PC) */
    if (psDevVAddrConfig->uiPCIndexMask != 0)
    {
        auiStartArray[i] = _CalcPCEIdx(sDevVAddrStart, psDevVAddrConfig, IMG_FALSE);
        auiEndArray[i] = _CalcPCEIdx(sDevVAddrEnd, psDevVAddrConfig, IMG_TRUE);
        auiEntriesPerPx[i] = psDevVAddrConfig->uiNumEntriesPC;
        apsConfig[i] = psDevAttrs->psBaseConfig;
        aeMMULevel[i] = MMU_LEVEL_3;
        i++;
    }

    /* Level 2 (PD) */
    if (psDevVAddrConfig->uiPDIndexMask != 0)
    {
        auiStartArray[i] = _CalcPDEIdx(sDevVAddrStart, psDevVAddrConfig, IMG_FALSE);
        auiEndArray[i] = _CalcPDEIdx(sDevVAddrEnd, psDevVAddrConfig, IMG_TRUE);
        auiEntriesPerPx[i] = psDevVAddrConfig->uiNumEntriesPD;
        if (i == 0)
        {
            apsConfig[i] = psDevAttrs->psBaseConfig;
        }
        else
        {
            apsConfig[i] = psMMUPDEConfig;
        }
        aeMMULevel[i] = MMU_LEVEL_2;
        i++;
    }

    /* Level 1 (PT) - 总是存在 */
    auiStartArray[i] = _CalcPTEIdx(sDevVAddrStart, psDevVAddrConfig, IMG_FALSE);
    if (psDevVAddrConfig->uiPTIndexMask != 0)
    {
        auiEndArray[i] = _CalcPTEIdx(sDevVAddrEnd, psDevVAddrConfig, IMG_TRUE);
    }
    else
    {
        /* PT掩码为0意味着只有1个PTE */
        auiEndArray[i] = auiStartArray[i] + 1;
    }

    auiEntriesPerPx[i] = psDevVAddrConfig->uiNumEntriesPT;

    if (i == 0)
    {
        apsConfig[i] = psDevAttrs->psBaseConfig;
    }
    else
    {
        apsConfig[i] = psMMUPTEConfig;
    }
    aeMMULevel[i] = MMU_LEVEL_1;
}

数据结构示例

假设分配虚拟地址范围:0x40201000 - 0x40203FFF (3个4KB页)

输出数组:
auiStartArray[] = {0x100, 0x201, 0x001}  // PC, PD, PT 起始索引
auiEndArray[]   = {0x100, 0x201, 0x003}  // PC, PD, PT 结束索引
auiEntriesPerPx[] = {512, 512, 512}      // 每级的条目数
aeMMULevel[]    = {LEVEL_3, LEVEL_2, LEVEL_1}

虚拟地址分解:
0x40201000
  ├── PC索引: 0x100
  ├── PD索引: 0x201
  └── PT索引: 0x001

0x40203FFF
  ├── PC索引: 0x100 (相同)
  ├── PD索引: 0x201 (相同)
  └── PT索引: 0x003

页面映射:MMU_MapPages

这是用户空间最常用的函数之一。

PVRSRV_ERROR
MMU_MapPages(MMU_CONTEXT *psMMUContext,
             PVRSRV_MEMALLOCFLAGS_T uiMappingFlags,
             IMG_DEV_VIRTADDR sDevVAddrBase,
             PMR *psPMR,
             IMG_UINT32 ui32PhysPgOffset,
             IMG_UINT32 ui32MapPageCount,
             IMG_UINT32 *paui32MapIndices,
             IMG_UINT32 uiLog2HeapPageSize)
{
    PVRSRV_ERROR eError;
    IMG_HANDLE hPriv;
    MMU_Levelx_INFO *psLevel = NULL;
    MMU_Levelx_INFO *psPrevLevel = NULL;
    IMG_UINT32 uiPTEIndex = 0;
    IMG_UINT32 uiPageSize = (1 << uiLog2HeapPageSize);
    IMG_UINT32 uiLoop = 0;
    IMG_DEVMEM_OFFSET_T uiPgOffset = 0;
    IMG_UINT32 uiFlushEnd = 0, uiFlushStart = 0;
    IMG_UINT64 uiProtFlags = 0, uiProtFlagsReadOnly = 0, uiDefProtFlags=0;
    MMU_PROTFLAGS_T uiMMUProtFlags = 0;
    const MMU_PxE_CONFIG *psConfig;
    const MMU_DEVVADDR_CONFIG *psDevVAddrConfig;
    IMG_DEV_VIRTADDR sDevVAddr = sDevVAddrBase;
    IMG_DEV_PHYADDR asDevPAddr[PMR_MAX_TRANSLATION_STACK_ALLOC];
    IMG_BOOL abValid[PMR_MAX_TRANSLATION_STACK_ALLOC];
    IMG_DEV_PHYADDR *psDevPAddr;
    IMG_DEV_PHYADDR sDevPAddr;
    IMG_BOOL *pbValid;
    IMG_BOOL bValid;
    IMG_BOOL bScratchBacking = IMG_FALSE, bZeroBacking = IMG_FALSE;
    PVRSRV_DEVICE_NODE *psDevNode;

映射流程图

核心映射循环

#if defined(PDUMP)
    PDUMPCOMMENT(psDevNode, "Wire up Page Table entries...");
#endif

    /* 参数验证 */
    PVR_LOG_RETURN_IF_INVALID_PARAM(psMMUContext != NULL, "psMMUContext");
    PVR_LOG_RETURN_IF_INVALID_PARAM(psPMR != NULL, "psPMR");

    psDevNode = psMMUContext->psPhysMemCtx->psDevNode;

    /* 分配地址和有效性数组 */
    if (ui32MapPageCount > PMR_MAX_TRANSLATION_STACK_ALLOC)
    {
        psDevPAddr = OSAllocMem(ui32MapPageCount * sizeof(IMG_DEV_PHYADDR));
        PVR_LOG_GOTO_IF_NOMEM(psDevPAddr, eError, ErrReturnError);

        pbValid = OSAllocMem(ui32MapPageCount * sizeof(IMG_BOOL));
        PVR_LOG_GOTO_IF_NOMEM(pbValid, eError, ErrFreePAddrMappingArray);
    }
    else
    {
        psDevPAddr = asDevPAddr;
        pbValid = abValid;
    }

    /* 获取物理地址 */
    if (NULL == paui32MapIndices)  /* 连续映射 */
    {
        eError = PMR_DevPhysAddr(psPMR,
                                 uiLog2HeapPageSize,
                                 ui32MapPageCount,
                                 ((IMG_DEVMEM_OFFSET_T)ui32PhysPgOffset << uiLog2HeapPageSize),
                                 psDevPAddr,
                                 pbValid,
                                 DEVICE_USE | MAPPING_USE);
        PVR_GOTO_IF_ERROR(eError, ErrFreeValidArray);
    }

    /* 获取页表配置 */
    _MMU_GetPTConfig(psMMUContext,
                     (IMG_UINT32)uiLog2HeapPageSize,
                     &psConfig,
                     &hPriv,
                     &psDevVAddrConfig);

    /* 转换标志 */
    eError = _MMU_ConvertDevMemFlags(IMG_FALSE,
                                     uiMappingFlags,
                                     &uiMMUProtFlags,
                                     psMMUContext);
    PVR_GOTO_IF_ERROR(eError, ErrPutPTConfig);

    /* 获取保护标志 */
    if (psConfig->uiBytesPerEntry == 8)
    {
        uiProtFlags = psMMUContext->psDevAttrs->pfnDerivePTEProt8(
            uiMMUProtFlags, uiLog2HeapPageSize);
        uiMMUProtFlags |= MMU_PROTFLAGS_READABLE;
        uiProtFlagsReadOnly = psMMUContext->psDevAttrs->pfnDerivePTEProt8(
            (uiMMUProtFlags & ~MMU_PROTFLAGS_WRITEABLE),
            uiLog2HeapPageSize);
    }
    else if (psConfig->uiBytesPerEntry == 4)
    {
        uiProtFlags = psMMUContext->psDevAttrs->pfnDerivePTEProt4(uiMMUProtFlags);
        uiMMUProtFlags |= MMU_PROTFLAGS_READABLE;
        uiProtFlagsReadOnly = psMMUContext->psDevAttrs->pfnDerivePTEProt4(
            (uiMMUProtFlags & ~MMU_PROTFLAGS_WRITEABLE));
    }

    /* 检查稀疏内存backing */
    if (PMR_IsSparse(psPMR))
    {
        bScratchBacking = PVRSRV_IS_SPARSE_SCRATCH_BACKING_REQUIRED(uiMappingFlags);
        if (bScratchBacking)
        {
            bZeroBacking = PVRSRV_IS_ZERO_BACKING_REQUIRED(uiMappingFlags);
        }
    }

#if defined(SUPPORT_PMR_DEFERRED_FREE)
    PMRMarkForDeferFree(psPMR);
#endif

    OSLockAcquire(psMMUContext->hLock);

    /* 映射循环 */
    for (uiLoop = 0; uiLoop < ui32MapPageCount; uiLoop++)
    {
#if defined(PDUMP)
        IMG_DEVMEM_OFFSET_T uiNextSymName;
#endif

        /* 索引映射需要单独获取物理地址 */
        if (NULL != paui32MapIndices)
        {
            uiPgOffset = paui32MapIndices[uiLoop];
            sDevVAddr.uiAddr = sDevVAddrBase.uiAddr + (uiPgOffset * uiPageSize);

            eError = PMR_DevPhysAddr(psPMR,
                                     uiLog2HeapPageSize,
                                     1,
                                     uiPgOffset * uiPageSize,
                                     &sDevPAddr,
                                     &bValid,
                                     DEVICE_USE | MAPPING_USE);
            PVR_GOTO_IF_ERROR(eError, ErrUnlockAndUnmapPages);
        }
        else
        {
            uiPgOffset = uiLoop + ui32PhysPgOffset;
            sDevPAddr = psDevPAddr[uiLoop];
            bValid = pbValid[uiLoop];
        }

        uiDefProtFlags = uiProtFlags;

        /* 处理有效或backing页面 */
        if (bValid || bScratchBacking)
        {
            if (!bValid)
            {
                /* 使用backing页面 */
                if (bZeroBacking)
                {
                    eError = _MMU_GetBackingPage(psDevNode,
                                                 &sDevPAddr.uiAddr,
                                                 DEV_ZERO_PAGE);
                    uiDefProtFlags = uiProtFlagsReadOnly;  /* 零页只读 */
                }
                else
                {
                    eError = _MMU_GetBackingPage(psDevNode,
                                                 &sDevPAddr.uiAddr,
                                                 SCRATCH_PAGE);
                }
                PVR_LOG_GOTO_IF_ERROR(eError, "_MMU_GetBackingPage",
                                      ErrUnlockAndUnmapPages);
            }
            else
            {
                /* 检查物理地址对齐 */
                PVR_ASSERT((sDevPAddr.uiAddr & (uiPageSize-1)) == 0);
            }

#if defined(DEBUG)
            /* 检查物理地址位宽 */
            {
                IMG_INT32 i32FeatureVal = PVRSRV_GET_DEVICE_FEATURE_VALUE(
                    psDevNode, PHYS_BUS_WIDTH);
                if (i32FeatureVal > 0)
                {
                    IMG_UINT32 ui32BitLength = FloorLog2(sDevPAddr.uiAddr);
                    if (ui32BitLength > (IMG_UINT32)i32FeatureVal)
                    {
                        PVR_DPF((PVR_DBG_ERROR,
                                "%s Failed. Physical address bitlength (%u) > chip (%d).",
                                __func__, ui32BitLength, i32FeatureVal));
                        PVR_ASSERT(ui32BitLength <= (IMG_UINT32)i32FeatureVal);
                        eError = PVRSRV_ERROR_INVALID_PARAMS;
                        goto ErrUnlockAndUnmapPages;
                    }
                }
            }
#endif

#if defined(PDUMP)
            if (bValid)
            {
                eError = PMR_PDumpSymbolicAddr(psPMR, uiPgOffset * uiPageSize,
                                               sizeof(aszMemspaceName), &aszMemspaceName[0],
                                               sizeof(aszSymbolicAddress), &aszSymbolicAddress[0],
                                               &uiSymbolicAddrOffset,
                                               &uiNextSymName);
                PVR_LOG_IF_ERROR(eError, "PMR_PDumpSymbolicAddr");
            }
#endif

            psPrevLevel = psLevel;

            /* 获取PTE信息 */
            if (!_MMU_GetPTInfo(psMMUContext, sDevVAddr, psDevVAddrConfig,
                                &psLevel, &uiPTEIndex))
            {
                PVR_GOTO_WITH_ERROR(eError, PVRSRV_ERROR_MAPPING_NOT_FOUND,
                                    ErrUnlockAndUnmapPages);
            }

            /* 更新刷新范围 */
            if (psPrevLevel == psLevel)
            {
                if (uiPTEIndex > uiFlushEnd)
                    uiFlushEnd = uiPTEIndex;
                else if (uiPTEIndex < uiFlushStart)
                    uiFlushStart = uiPTEIndex;
            }
            else
            {
                /* 刷新前一个PT */
                if (psPrevLevel != NULL)
                {
                    eError = PhysHeapPagesClean(psDevNode->psMMUPhysHeap,
                                                &psPrevLevel->sMemDesc.psMapping->sMemHandle,
                                                uiFlushStart * psConfig->uiBytesPerEntry + 
                                                psPrevLevel->sMemDesc.uiOffset,
                                                (uiFlushEnd+1 - uiFlushStart) * 
                                                psConfig->uiBytesPerEntry);
                    PVR_GOTO_IF_ERROR(eError, ErrUnlockAndUnmapPages);
                }

                uiFlushStart = uiPTEIndex;
                uiFlushEnd = uiFlushStart;
            }

            HTBLOGK(HTB_SF_MMU_PAGE_OP_MAP, ...);

            /* 设置PTE */
            eError = _SetupPTE(psMMUContext,
                               psLevel,
                               uiPTEIndex,
                               psConfig,
                               &sDevPAddr,
                               &sDevVAddr,
                               IMG_FALSE,
#if defined(PDUMP)
                               (bValid)?aszMemspaceName:
                                   (psMMUContext->psDevAttrs->pszMMUPxPDumpMemSpaceName),
                               (bValid)?aszSymbolicAddress:
                                   ((bZeroBacking)?DEV_ZERO_PAGE_STR:SCRATCH_PAGE_STR),
                               (bValid)?uiSymbolicAddrOffset:0,
#endif
                               uiDefProtFlags);
            PVR_LOG_GOTO_IF_ERROR(eError, "_SetupPTE", ErrUnlockAndUnmapPages);

            if (bValid)
            {
                PVR_ASSERT(psLevel->ui32RefCount <= psLevel->ui32NumOfEntries);
            }
        }

        sDevVAddr.uiAddr += uiPageSize;
    }

    /* 刷新最后一个PT */
    if (psLevel != NULL)
    {
        eError = PhysHeapPagesClean(psDevNode->psMMUPhysHeap,
                                    &psLevel->sMemDesc.psMapping->sMemHandle,
                                    uiFlushStart * psConfig->uiBytesPerEntry +
psLevel->sMemDesc.uiOffset,
                                    (uiFlushEnd+1 - uiFlushStart) *
psConfig->uiBytesPerEntry);
        PVR_GOTO_IF_ERROR(eError, ErrUnlockAndUnmapPages);
    }
    /* 失效MMU TLB */
    psDevNode->pfnMMUCacheInvalidate(psDevNode,
                                     psMMUContext,
                                     MMU_LEVEL_1,
                                     IMG_FALSE);

    /* 如果需要立即失效 */
    if (PVRSRV_CHECK_KICK_PT_INVALIDATE(uiMappingFlags) &&
        psDevNode->pfnMMUCacheInvalidateKickAndWait != NULL)
    {
        eError = psDevNode->pfnMMUCacheInvalidateKickAndWait(psDevNode);
        PVR_LOG_GOTO_IF_ERROR(eError, "pfnMMUCacheInvalidateKickAndWait", 
                              ErrUnlockAndUnmapPages);
    }

    OSLockRelease(psMMUContext->hLock);

    _MMU_PutPTConfig(psMMUContext, hPriv);

    if (psDevPAddr != asDevPAddr)
    {
        OSFreeMem(pbValid);
        OSFreeMem(psDevPAddr);
    }
#if defined(PDUMP)
    PDUMPCOMMENT(psDevNode, "Wired up %d Page Table entries (out of %d)",
    ui32MappedCount, ui32MapPageCount);
#endif

    return PVRSRV_OK;

ErrUnlockAndUnmapPages:
    (void)MMU_UnmapPagesUnlocked(psMMUContext,
                                PMR_IsSparse(psPMR) ? uiMappingFlags : 0,
                                sDevVAddrBase,
                                uiLoop,
                                paui32MapIndices,
                                uiLog2HeapPageSize);
    OSLockRelease(psMMUContext->hLock);
ErrPutPTConfig:
    _MMU_PutPTConfig(psMMUContext, hPriv);
ErrFreeValidArray:
    if (psDevPAddr != asDevPAddr)
    {
        OSFreeMem(pbValid);
    }
ErrFreePAddrMappingArray:
    if (psDevPAddr != asDevPAddr)
    {
        OSFreeMem(psDevPAddr);
    }
ErrReturnError:
    return eError;
}

快速映射:MMU_MapPMRFast

针对连续PMR的优化版本。

PVRSRV_ERROR
MMU_MapPMRFast(MMU_CONTEXT *psMMUContext,
               IMG_DEV_VIRTADDR sDevVAddrBase,
               PMR *psPMR,
               IMG_DEVMEM_SIZE_T uiSizeBytes,
               PVRSRV_MEMALLOCFLAGS_T uiMappingFlags,
               IMG_UINT32 uiLog2HeapPageSize,
               MMU_PTE_REMAP_POLICY eRemapPolicy)
{
    PVRSRV_ERROR eError = PVRSRV_OK;
    PVRSRV_DEVICE_NODE *psDevNode = psMMUContext->psPhysMemCtx->psDevNode;
    const MMU_PxE_CONFIG *psConfig;
    const MMU_DEVVADDR_CONFIG *psDevVAddrConfig;
    IMG_HANDLE hPriv;

    IMG_UINT32 i, uiChunkStart, uiLastPTEIndex, uiNumEntriesToWrite;
    IMG_UINT32 ui32PagesDone=0, uiPTEIndex=0;

    IMG_UINT8 uiAddrLog2Align, uiAddrShift, uiParityShift;
    IMG_UINT64 uiAddrMask, uiProtFlags;
    IMG_UINT32 uiBytesPerEntry;
    IMG_UINT64 uiParityBit = 0;
    IMG_BOOL bSetParity = IMG_FALSE;
    IMG_DEV_VIRTADDR sDevVAddrRunning, sDevVAddrBaseCopy = sDevVAddrBase;

    IMG_UINT64* pui64LevelBase;
    IMG_UINT32* pui32LevelBase;
    MMU_PROTFLAGS_T uiMMUProtFlags = 0;
    MMU_Levelx_INFO *psLevel = NULL;

    IMG_DEV_PHYADDR asDevPAddr[PMR_MAX_TRANSLATION_STACK_ALLOC];
    IMG_BOOL abValid[PMR_MAX_TRANSLATION_STACK_ALLOC];
    IMG_UINT32 uiNumPages = uiSizeBytes >> uiLog2HeapPageSize;

    IMG_BOOL bValidateOrTweak = psDevNode->pfnValidateOrTweakPhysAddrs ? 
                                IMG_TRUE : IMG_FALSE;

快速映射优化

批量写入循环

    /* 获取配置 */
    _MMU_GetPTConfig(psMMUContext, (IMG_UINT32)uiLog2HeapPageSize,
                     &psConfig, &hPriv, &psDevVAddrConfig);

    eError = _MMU_ConvertDevMemFlags(IMG_FALSE,
                                     uiMappingFlags,
                                     &uiMMUProtFlags,
                                     psMMUContext);
    PVR_GOTO_IF_ERROR(eError, put_mmu_context);

    /* 提取配置常量以避免重复解引用 */
    uiAddrLog2Align = psConfig->uiAddrLog2Align;
    uiAddrShift = psConfig->uiAddrShift;
    uiAddrMask = psConfig->uiAddrMask;
    uiBytesPerEntry = psConfig->uiBytesPerEntry;

    bSetParity = psConfig->uiParityBitMask;
    uiParityShift = psConfig->uiParityBitShift;

    sDevVAddrRunning.uiAddr = sDevVAddrBase.uiAddr;

    /* 计算保护标志 */
    if (uiBytesPerEntry == 8)
    {
        uiProtFlags = psMMUContext->psDevAttrs->pfnDerivePTEProt8(
            uiMMUProtFlags, uiLog2HeapPageSize);
    }
    else if (uiBytesPerEntry == 4)
    {
        uiProtFlags = psMMUContext->psDevAttrs->pfnDerivePTEProt4(uiMMUProtFlags);
    }
    else
    {
        PVR_LOG_GOTO_WITH_ERROR("psConfig->uiBytesPerEntry", eError, 
                               PVRSRV_ERROR_MMU_CONFIG_IS_WRONG, put_mmu_context);
    }

#if defined(SUPPORT_PMR_DEFERRED_FREE)
    PMRMarkForDeferFree(psPMR);
#endif

    OSLockAcquire(psMMUContext->hLock);

    do
    {
        /* 定位PT */
        if (!_MMU_GetPTInfo(psMMUContext, sDevVAddrBase, psDevVAddrConfig,
                            &psLevel, &uiPTEIndex))
        {
            PVR_GOTO_WITH_ERROR(eError, PVRSRV_ERROR_MAPPING_NOT_FOUND,
                                unlock_mmu_context);
        }

        pui64LevelBase = (IMG_UINT64*)psLevel->sMemDesc.pvCpuVAddr;
        pui32LevelBase = (IMG_UINT32*)psLevel->sMemDesc.pvCpuVAddr;

        /* 计算要写入的条目数 */
        uiLastPTEIndex = MIN(uiPTEIndex + uiNumPages - ui32PagesDone, 
                            psDevVAddrConfig->uiNumEntriesPT);
        uiNumEntriesToWrite = uiLastPTEIndex - uiPTEIndex;

        /* 批量获取物理地址 */
        for (uiChunkStart = 0; uiChunkStart < uiNumEntriesToWrite; 
             uiChunkStart += PMR_MAX_TRANSLATION_STACK_ALLOC)
        {
            IMG_UINT32 uiNumPagesInBlock = MIN(uiNumEntriesToWrite - uiChunkStart, 
                                              PMR_MAX_TRANSLATION_STACK_ALLOC);

            eError = PMR_DevPhysAddr(psPMR,
                                     uiLog2HeapPageSize,
                                     uiNumPagesInBlock,
                                     (IMG_UINT64)(ui32PagesDone + uiChunkStart) << 
                                     uiLog2HeapPageSize,
                                     asDevPAddr,
                                     abValid,
                                     DEVICE_USE | MAPPING_USE);
            PVR_GOTO_IF_ERROR(eError, unlock_mmu_context);

            /* 验证/调整物理地址 */
            if (bValidateOrTweak)
            {
                for (i=0; i<uiNumPagesInBlock; i++)
                {
                    PVRSRV_ERROR eErr = psDevNode->pfnValidateOrTweakPhysAddrs(
                        psDevNode,
                        psMMUContext->psDevAttrs,
                        &asDevPAddr[i].uiAddr);
                    PVR_GOTO_IF_ERROR(eErr, unlock_mmu_context);
                }
            }

            /* 批量写入PTE */
            if (uiBytesPerEntry == 8)
            {
                for (i=0; i<uiNumPagesInBlock; i++)
                {
                    if (bSetParity)
                    {
                        uiParityBit = _GetParityBit(sDevVAddrRunning.uiAddr ^ 
                                                    asDevPAddr[i].uiAddr);
                        uiParityBit <<= uiParityShift;
                        sDevVAddrRunning.uiAddr += (IMG_UINT64_C(1) << uiLog2HeapPageSize);
                    }

                    /* 检查重映射策略 */
                    if (eRemapPolicy == MMU_PTE_REMAP_POLICY_BLOCK &&
                        pui64LevelBase[uiPTEIndex + uiChunkStart + i] & 
                        psConfig->uiValidEnMask &&
                        (uiProtFlags & psConfig->uiValidEnMask))
                    {
                        PVR_GOTO_WITH_ERROR(eError, PVRSRV_ERROR_MMU_REMAP_BLOCKED, 
                                           unlock_mmu_context);
                    }

                    /* 写入PTE */
                    pui64LevelBase[uiPTEIndex + uiChunkStart + i] =
                        (((asDevPAddr[i].uiAddr >> uiAddrLog2Align) << uiAddrShift) & 
                        uiAddrMask) | uiProtFlags | uiParityBit;
                }
            }
            else if (uiBytesPerEntry == 4)
            {
                for (i=0; i<uiNumPagesInBlock; i++)
                {
                    if (eRemapPolicy == MMU_PTE_REMAP_POLICY_BLOCK &&
                        pui32LevelBase[uiPTEIndex + uiChunkStart + i] & 
                        (IMG_UINT32)psConfig->uiValidEnMask &&
                        (uiProtFlags & (IMG_UINT32)psConfig->uiValidEnMask))
                    {
                        PVR_GOTO_WITH_ERROR(eError, PVRSRV_ERROR_MMU_REMAP_BLOCKED, 
                                           unlock_mmu_context);
                    }

                    pui32LevelBase[uiPTEIndex + uiChunkStart + i] =
                        (((asDevPAddr[i].uiAddr >> uiAddrLog2Align) << uiAddrShift) & 
                        uiAddrMask) | uiProtFlags;
                }
            }

#if defined(PDUMP)
            /* PDump批量条目 */
            for (i=0; i<uiNumPagesInBlock; i++)
            {
                IMG_DEVMEM_OFFSET_T uiNextSymName;
                IMG_UINT64 uiParityBit = psConfig->uiParityBitMask ? 
                    _GetParityBit(sDevVAddrRunningPdump.uiAddr ^ 0ULL) : 0;

                eError = PMR_PDumpSymbolicAddr(...);
                PDumpMMUDumpPxEntries(...);

                sDevVAddrRunningPdump.uiAddr += (1 << uiLog2HeapPageSize);
            }
#endif

#if defined(PVRSRV_ENABLE_HTB)
            /* HTB日志 */
            if (bHTBLog)
            {
                for (i=0; i<uiNumPagesInBlock; i++)
                {
                    HTBLOGK(HTB_SF_MMU_PAGE_OP_PMRMAP, ...);
                    HTBLOGK(HTB_SF_MMU_PAGE_OP_TABLE, ...);
                }
            }
#endif
        }

        /* 刷新这批PTE */
        eError = PhysHeapPagesClean(psDevNode->psMMUPhysHeap,
                                    &psLevel->sMemDesc.psMapping->sMemHandle,
                                    uiPTEIndex * uiBytesPerEntry + 
                                    psLevel->sMemDesc.uiOffset,
                                    (uiNumEntriesToWrite) * uiBytesPerEntry);
        PVR_GOTO_IF_ERROR(eError, unlock_mmu_context);

        sDevVAddrBase.uiAddr += uiNumEntriesToWrite * (1 << uiLog2HeapPageSize);
        ui32PagesDone += uiNumEntriesToWrite;

    } while (ui32PagesDone < uiNumPages);

    OSLockRelease(psMMUContext->hLock);

    /* 失效MMU TLB */
    psDevNode->pfnMMUCacheInvalidate(psDevNode,
                                     psMMUContext,
                                     MMU_LEVEL_1,
                                     IMG_FALSE);

    _MMU_PutPTConfig(psMMUContext, hPriv);

    return PVRSRV_OK;

unlock_mmu_context:
    if (eError != PVRSRV_ERROR_MMU_REMAP_BLOCKED)
    {
        (void)MMU_UnmapPMRFastUnlocked(psMMUContext,
                                       sDevVAddrBaseCopy,
                                       uiNumPages,
                                       uiLog2HeapPageSize);
    }
    OSLockRelease(psMMUContext->hLock);
put_mmu_context:
    _MMU_PutPTConfig(psMMUContext, hPriv);

    return eError;
}

性能对比

映射1000个页面的性能对比:

传统方法 (MMU_MapPages):
- 1000次 PT查找
- 1000次 PTE设置
- 约1000次 缓存刷新操作
总时间:~10ms

快速方法 (MMU_MapPMRFast):
- 2-3次 PT查找(跨越PT边界)
- 1次批量获取物理地址
- 直接内存写入PTE
- 2-3次批量缓存刷新
总时间:~2ms

加速比:5x

快速取消映射

static PVRSRV_ERROR MMU_UnmapPMRFastUnlocked(MMU_CONTEXT *psMMUContext,
                                             IMG_DEV_VIRTADDR sDevVAddrBase,
                                             IMG_UINT32 ui32PageCount,
                                             IMG_UINT32 uiLog2PageSize)
{
    IMG_UINT32 uiPTEIndex = 0, uiLastPTEIndex = 0, ui32PagesDone = 0;
    IMG_UINT32 uiPageSize = 1 << uiLog2PageSize;
    MMU_Levelx_INFO *psLevel = NULL;
    void* pvPTStart;
    const MMU_PxE_CONFIG *psConfig;
    const MMU_DEVVADDR_CONFIG *psDevVAddrConfig;
    IMG_UINT64 uiEntry = 0;

    // 获取配置
    _MMU_GetPTConfig(psMMUContext, uiLog2PageSize, &psConfig, &hPriv, &psDevVAddrConfig);

    // 预计算无效页表项值
    if (psConfig->uiBytesPerEntry == 8) {
        IMG_UINT64 ui64BadPhysAddr = gsBadDevPhyAddr.uiAddr & ~(psConfig->uiProtMask | psConfig->uiParityBitMask);
        uiEntry = ui64BadPhysAddr | uiProtFlags;
    } else if (psConfig->uiBytesPerEntry == 4) {
        uiEntry = (((IMG_UINT32) gsBadDevPhyAddr.uiAddr) & ~psConfig->uiProtMask) | (IMG_UINT32) uiProtFlags;
    }

    do {
        // 获取页表信息
        if (!_MMU_GetPTInfo(psMMUContext, sDevVAddr, psDevVAddrConfig,
                            &psLevel, &uiPTEIndex)) {
            PVR_GOTO_WITH_ERROR(eError, PVRSRV_ERROR_MAPPING_NOT_FOUND, e0);
        }

        pvPTStart = psLevel->sMemDesc.pvCpuVAddr;
        uiLastPTEIndex = MIN(uiPTEIndex + ui32PageCount - ui32PagesDone, 
                             psDevVAddrConfig->uiNumEntriesPT);
        uiNumEntriesToWrite = uiLastPTEIndex - uiPTEIndex;

        // 批量写入无效项
        if (psConfig->uiBytesPerEntry == 8) {
            for (i = uiPTEIndex; i < uiLastPTEIndex; i++) {
                IMG_UINT64 uiParityBit = 0;
                #if defined(PVRSRV_MMU_PARITY_ON_PTALLOC_AND_PTEUNMAP)
                if (bSetParity) {
                    uiParityBit = psMMUContext->psDevAttrs->pui64PrecomputedAllocParity[uiParityPatternIdx][i] ^ 
                                  ui64BadPhysAddrParity;
                }
                #endif
                ((IMG_UINT64*)pvPTStart)[i] = uiEntry | uiParityBit;
            }
        } else {
            for (i = uiPTEIndex; i < uiLastPTEIndex; i++) {
                ((IMG_UINT32*)pvPTStart)[i] = (IMG_UINT32) uiEntry;
            }
        }

        // 缓存清理
        PhysHeapPagesClean(psDevNode->psMMUPhysHeap, &psLevel->sMemDesc.psMapping->sMemHandle,
                           uiPTEIndex * psConfig->uiBytesPerEntry + psLevel->sMemDesc.uiOffset,
                           uiNumEntriesToWrite * psConfig->uiBytesPerEntry);

        sDevVAddr.uiAddr += uiNumEntriesToWrite * uiPageSize;
        ui32PagesDone += uiNumEntriesToWrite;

    } while (ui32PagesDone < ui32PageCount);

    _MMU_PutPTConfig(psMMUContext, hPriv);

    // TLB刷新
    psDevNode->pfnMMUCacheInvalidate(psDevNode, psMMUContext, MMU_LEVEL_1, IMG_TRUE);

    return PVRSRV_OK;
}

快速取消映射特点:
- 预计算无效值: 避免在循环中重复计算
- 批量写入: 简单的循环写入操作
- 奇偶校验优化: 使用预计算的奇偶校验模式

重要的公共API

MMU上下文创建:MMU_ContextCreate

PVRSRV_ERROR
MMU_ContextCreate(CONNECTION_DATA *psConnection,
                  PVRSRV_DEVICE_NODE *psDevNode,
                  MMU_CONTEXT **ppsMMUContext,
                  MMU_DEVICEATTRIBS *psDevAttrs)
{
    MMU_CONTEXT *psMMUContext;
    const MMU_DEVVADDR_CONFIG *psDevVAddrConfig;
    const MMU_PxE_CONFIG *psConfig;
    MMU_PHYSMEM_CONTEXT *psPhysMemCtx;
    IMG_UINT32 ui32BaseObjects;
    IMG_UINT32 ui32Size;
    IMG_CHAR sBuf[40];
    PVRSRV_ERROR eError = PVRSRV_OK;

#if defined(PDUMP)
    PDUMPCOMMENT(psDevNode, "MMU context create");
#endif

    psConfig = psDevAttrs->psBaseConfig;
    psDevVAddrConfig = psDevAttrs->psTopLevelDevVAddrConfig;

    /* 确定基础级别的条目数 */
    switch (psDevAttrs->psBaseConfig->ePxLevel)
    {
        case MMU_LEVEL_3:
            ui32BaseObjects = psDevVAddrConfig->uiNumEntriesPC;
            break;
        case MMU_LEVEL_2:
            ui32BaseObjects = psDevVAddrConfig->uiNumEntriesPD;
            break;
        case MMU_LEVEL_1:
            ui32BaseObjects = psDevVAddrConfig->uiNumEntriesPT;
            break;
        default:
            PVR_LOG_GOTO_WITH_ERROR("psDevAttrs->psBaseConfig->ePxLevel", eError, 
                                   PVRSRV_ERROR_INVALID_PARAMS, e0);
    }

    /* 分配MMU上下文(包括基础级别的指针数组) */
    ui32Size = sizeof(MMU_CONTEXT) +
               ((ui32BaseObjects - 1) * sizeof(MMU_Levelx_INFO *));

    psMMUContext = OSAllocZMem(ui32Size);
    PVR_LOG_GOTO_IF_NOMEM(psMMUContext, eError, e0);

    psMMUContext->psDevAttrs = psDevAttrs;

    /* 分配物理内存上下文 */
    psPhysMemCtx = OSAllocZMem(sizeof(MMU_PHYSMEM_CONTEXT));
    PVR_LOG_GOTO_IF_NOMEM(psPhysMemCtx, eError, e1);

    psMMUContext->psPhysMemCtx = psPhysMemCtx;
    psMMUContext->psConnection = psConnection;

    psPhysMemCtx->psDevNode = psDevNode;
    psPhysMemCtx->psMMUContext = psMMUContext;

    /* 设置OS ID(如果支持) */
#if defined(SUPPORT_CUSTOM_OSID_EMISSION)
    if (!_MMU_IS_FWKM_CTX(psMMUContext))
    {
        psPhysMemCtx->ui32OSid = psConnection->ui32OSid;
        psPhysMemCtx->ui32OSidReg = psConnection->ui32OSidReg;
        psPhysMemCtx->bOSidAxiProt = psConnection->bOSidAxiProtReg;
    }
#endif

    /* 创建RA名称 */
    OSSNPrintf(sBuf, sizeof(sBuf), "pgtables %p", psPhysMemCtx);
    psPhysMemCtx->uiPhysMemRANameAllocSize = OSStringLength(sBuf)+1;
    psPhysMemCtx->pszPhysMemRAName = OSAllocMem(
        psPhysMemCtx->uiPhysMemRANameAllocSize);
    PVR_LOG_GOTO_IF_NOMEM(psPhysMemCtx->pszPhysMemRAName, eError, e2);

    OSStringSafeCopy(psPhysMemCtx->pszPhysMemRAName, sBuf, 
                     psPhysMemCtx->uiPhysMemRANameAllocSize);

    /* 创建RA */
    psPhysMemCtx->psPhysMemRA = RA_Create(
        psPhysMemCtx->pszPhysMemRAName,
        PhysHeapGetPageShift(psDevNode->psMMUPhysHeap),
        RA_LOCKCLASS_2,
        _MMU_PhysMem_RAImportAlloc,
        _MMU_PhysMem_RAImportFree,
        psPhysMemCtx,
        RA_POLICY_DEFAULT);

    if (psPhysMemCtx->psPhysMemRA == NULL)
    {
        OSFreeMem(psPhysMemCtx->pszPhysMemRAName);
        PVR_GOTO_WITH_ERROR(eError, PVRSRV_ERROR_OUT_OF_MEMORY, e3);
    }

    /* 创建清理数据 */
    psPhysMemCtx->psCleanupData = OSAllocMem(sizeof(*(psPhysMemCtx->psCleanupData)));
    PVR_LOG_GOTO_IF_NOMEM(psPhysMemCtx->psCleanupData, eError, e4);

    OSLockCreate(&psPhysMemCtx->psCleanupData->hCleanupLock);
    psPhysMemCtx->psCleanupData->bMMUContextExists = IMG_TRUE;
    dllist_init(&psPhysMemCtx->psCleanupData->sMMUCtxCleanupItemsHead);
    OSAtomicWrite(&psPhysMemCtx->psCleanupData->iRef, 1);

    /* 分配基础级别对象 */
    if (!_MMU_IS_FWKM_CTX_VZGUEST(psMMUContext))
    {
        if (_PxMemAlloc(psMMUContext,
                        ui32BaseObjects,
                        psConfig,
                        psDevAttrs->psBaseConfig->ePxLevel,
                        &psMMUContext->sBaseLevelInfo.sMemDesc,
#if defined(PVRSRV_MMU_PARITY_ON_PTALLOC_AND_PTEUNMAP)
                        NULL,
                        0U,
#endif
                        psDevAttrs->ui32BaseAlign))
        {
            PVR_LOG_GOTO_WITH_ERROR("_PxMemAlloc", eError, 
                                   PVRSRV_ERROR_OUT_OF_MEMORY, e5);
        }
    }

    dllist_init(&psMMUContext->psPhysMemCtx->sTmpMMUMappingHead);

    psMMUContext->sBaseLevelInfo.ui32NumOfEntries = ui32BaseObjects;
    psMMUContext->sBaseLevelInfo.ui32RefCount = 0;

    /* 设备特定的workaround */
    if (psDevNode->pfnMMUTopLevelPxWorkarounds != NULL)
    {
        MMU_MEMORY_DESC *psMemDesc = &psMMUContext->sBaseLevelInfo.sMemDesc;

        psDevNode->pfnMMUTopLevelPxWorkarounds(psConnection,
                                               psDevNode,
                                               psMemDesc->sDevPAddr,
                                               psMemDesc->pvCpuVAddr);

        eError = PhysHeapPagesClean(psDevNode->psMMUPhysHeap,
                                    &psMemDesc->psMapping->sMemHandle,
                                    psMemDesc->uiOffset,
                                    psMemDesc->uiSize);
        PVR_LOG_IF_ERROR(eError, "PhysHeapPagesClean");
    }

    /* 创建锁 */
    eError = OSLockCreate(&psMMUContext->hLock);
    PVR_LOG_GOTO_IF_ERROR(eError, "OSLockCreate", e6);

    *ppsMMUContext = psMMUContext;

    return PVRSRV_OK;

    /* 错误处理路径 */
e6:
    _PxMemFree(psMMUContext, &psMMUContext->sBaseLevelInfo.sMemDesc, 
               psDevAttrs->psBaseConfig->ePxLevel);
e5:
    OSFreeMem(psPhysMemCtx->psCleanupData);
e4:
    RA_Delete(psPhysMemCtx->psPhysMemRA);
e3:
    OSFreeMem(psPhysMemCtx->pszPhysMemRAName);
e2:
    OSFreeMem(psPhysMemCtx);
e1:
    OSFreeMem(psMMUContext);
e0:
    return eError;
}

创建流程图

MMU上下文销毁:MMU_ContextDestroy

void
MMU_ContextDestroy(MMU_CONTEXT *psMMUContext)
{
    PVRSRV_DATA *psPVRSRVData = PVRSRVGetPVRSRVData();
    PDLLIST_NODE psNode, psNextNode;
    PVRSRV_DEVICE_NODE *psDevNode = psMMUContext->psPhysMemCtx->psDevNode;
    MMU_CTX_CLEANUP_DATA *psCleanupData = psMMUContext->psPhysMemCtx->psCleanupData;

    PVR_DPF((PVR_DBG_MESSAGE, "%s: Enter", __func__));

    if (psPVRSRVData->eServicesState == PVRSRV_SERVICES_STATE_OK)
    {
        /* 确保没有活动页面 */
        PVR_ASSERT(psMMUContext->sBaseLevelInfo.ui32RefCount == 0);
    }

    /* 必须按正确顺序获取锁以避免死锁 */
    OSLockAcquire(psCleanupData->hCleanupLock);
    OSLockAcquire(psMMUContext->hLock);

    /* 释放顶级MMU对象 */
    if (!_MMU_IS_FWKM_CTX_VZGUEST(psMMUContext))
    {
        _PxMemFree(psMMUContext,
                   &psMMUContext->sBaseLevelInfo.sMemDesc,
                   psMMUContext->psDevAttrs->psBaseConfig->ePxLevel);
    }

    /* 清空临时延迟释放列表 */
    _FreeMMUMapping(psDevNode, &psMMUContext->psPhysMemCtx->sTmpMMUMappingHead);
    PVR_ASSERT(dllist_is_empty(&psMMUContext->psPhysMemCtx->sTmpMMUMappingHead));

    /* 清空延迟释放列表 */
    dllist_foreach_node(&psCleanupData->sMMUCtxCleanupItemsHead,
                        psNode,
                        psNextNode)
    {
        MMU_CLEANUP_ITEM *psCleanup = IMG_CONTAINER_OF(psNode,
                                                       MMU_CLEANUP_ITEM,
                                                       sMMUCtxCleanupItem);

        _FreeMMUMapping(psDevNode, &psCleanup->sMMUMappingHead);

        dllist_remove_node(psNode);
    }
    PVR_ASSERT(dllist_is_empty(&psCleanupData->sMMUCtxCleanupItemsHead));

    psCleanupData->bMMUContextExists = IMG_FALSE;

    /* 释放物理内存上下文 */
    RA_Delete(psMMUContext->psPhysMemCtx->psPhysMemRA);
    OSFreeMem(psMMUContext->psPhysMemCtx->pszPhysMemRAName);
    OSFreeMem(psMMUContext->psPhysMemCtx);

    OSLockRelease(psMMUContext->hLock);
    OSLockRelease(psCleanupData->hCleanupLock);

    /* 如果清理数据引用计数为0,释放它 */
    if (OSAtomicDecrement(&psCleanupData->iRef) == 0)
    {
        OSLockDestroy(psCleanupData->hCleanupLock);
        OSFreeMem(psCleanupData);
    }

    OSLockDestroy(psMMUContext->hLock);

    /* 释放上下文本身 */
    OSFreeMem(psMMUContext);

    PVR_DPF((PVR_DBG_MESSAGE, "%s: Exit", __func__));
}

整体工作流程总结

映射操作完整流程

解映射和释放流程

页表层次结构示例

虚拟地址:0x0000_4020_1FFF (1GB + 2MB + 8KB - 1)

三级页表结构 (4KB页):

┌─────────────────────────────────────┐
│ MMU_CONTEXT                         │
│ sBaseLevelInfo (Level 3 - PC)      │
│ ui32NumOfEntries = 512              │
└─────────┬───────────────────────────┘
          │
          ├─[0] → NULL
          ├─[1] → NULL
          ├─[2] → Level 2 (PD) ←── PC索引 = 2
          │       ┌──────────────────────┐
          │       │ ui32NumOfEntries=512 │
          │       │ sMemDesc (物理内存)  │
          │       └────┬─────────────────┘
          │            │
          │            ├─[0] → NULL
          │            ├─[1] → NULL  
          │            ├─[2] → Level 1 (PT) ←── PD索引 = 2
          │            │       ┌──────────────────────┐
          │            │       │ ui32NumOfEntries=512 │
          │            │       │ sMemDesc (物理内存)  │
          │            │       └────┬─────────────────┘
          │            │            │
          │            │            ├─[0] → PTE (物理页0) = 0x8000_0000
          │            │            ├─[1] → PTE (物理页1) = 0x8000_1000
          │            │            ├─[2] → PTE (物理页2) = 0x8000_2000 ←── PT索引 = 2
          │            │            │       ┌─────────────────────────┐
          │            │            │       │ 物理地址: 0x8000_2000   │
          │            │            │       │ 标志: Valid|Read|Write  │
          │            │            │       │ 奇偶校验位: 1           │
          │            │            │       └─────────────────────────┘
          │            │            └─[511] → ...
          │            └─[511] → ...
          └─[511] → ...

虚拟地址分解:
0x0000_4020_1FFF
= 0000_0000_0000_0000_0100_0000_0010_0000_0001_1111_1111_1111

PC索引 (bits 47-39): 000000010 = 2
PD索引 (bits 38-30): 000000010 = 2  
PT索引 (bits 29-21): 000000001 = 1
页内偏移 (bits 20-0): 00001_1111_1111_1111 = 0x1FFF (8KB - 1)

物理地址:0x8000_1000 + 0x1FFF = 0x8000_2FFF

内存对象生命周期

关键数据结构关系总结

MMU_CONTEXT
    │
    ├─── psDevAttrs (设备属性)
    │       ├─ psBaseConfig (基础配置)
    │       ├─ psTopLevelDevVAddrConfig (虚拟地址配置)
    │       └─ 各种回调函数
    │
    ├─── psPhysMemCtx (物理内存上下文)
    │       │
    │       ├─── psPhysMemRA (RA分配器)
    │       │       └─ 管理页表物理内存
    │       │
    │       ├─── psCleanupData (清理元数据)
    │       │       ├─ sMMUCtxCleanupItemsHead
    │       │       │     └─ 链接所有清理项
    │       │       └─ bMMUContextExists
    │       │
    │       └─── sTmpMMUMappingHead
    │               └─ 临时延迟释放列表
    │
    └─── sBaseLevelInfo (基础级别)
            │
            ├─── sMemDesc (内存描述符)
            │       ├─ sDevPAddr (设备物理地址)
            │       ├─ pvCpuVAddr (CPU虚拟地址)
            │       └─ psMapping
            │             ├─ sMemHandle (OS句柄)
            │             ├─ uiCpuVAddrRefCount
            │             └─ sMMUMappingItem (延迟释放节点)
            │
            └─── apsNextLevel[] (下一级数组)
                    └─ 递归结构,形成页表树

性能优化要点

/* 1. 批量操作避免重复锁 */
OSLockAcquire(psMMUContext->hLock);
for (i = 0; i < count; i++) {
    // 批量处理
}
OSLockRelease(psMMUContext->hLock);

/* 2. 缓存配置避免重复查询 */
_MMU_GetPTConfig(psMMUContext, uiLog2PageSize, ...);
// 使用缓存的配置
_MMU_PutPTConfig(psMMUContext, hPriv);

/* 3. 延迟刷新,批量刷新 */
// 不要每个PTE都刷新,而是记录范围
if (psPrevLevel != psLevel) {
    // 切换PT时才刷新
    PhysHeapPagesClean(..., uiFlushStart, uiFlushEnd);
}

/* 4. 栈数组避免动态分配 */
if (count <= 128) {
    // 使用栈数组
    asDevPAddr[128];
} else {
    // 动态分配
    psDevPAddr = OSAllocMem(...);
}

/* 5. 预计算奇偶校验模式 */
// 在初始化时预计算
pui64PrecomputedAllocParity[2][512];
// 使用时直接拷贝
OSCachedMemCopy(dest, precomputed, size);

错误处理模式

/* 统一的错误处理模式 */
PVRSRV_ERROR Function(...)
{
    PVRSRV_ERROR eError;
    Resource *psResource1 = NULL;
    Resource *psResource2 = NULL;

    // 分配资源1
    psResource1 = AllocResource1();
    PVR_LOG_GOTO_IF_NOMEM(psResource1, eError, e0);

    // 分配资源2
    psResource2 = AllocResource2();
    PVR_LOG_GOTO_IF_NOMEM(psResource2, eError, e1);

    // 执行操作
    eError = DoSomething();
    PVR_GOTO_IF_ERROR(eError, e2);

    return PVRSRV_OK;

    // 反向释放资源
e2:
    FreeResource2(psResource2);
e1:
    FreeResource1(psResource1);
e0:
    return eError;
}

并发控制

/* 锁的层次结构(避免死锁) */

Level 1: psCleanupData->hCleanupLock
    └─ Level 2: psMMUContext->hLock
        └─ Level 3: 其他资源锁

/* 正确的获取顺序 */
OSLockAcquire(psCleanupData->hCleanupLock);
OSLockAcquire(psMMUContext->hLock);
// ... 操作 ...
OSLockRelease(psMMUContext->hLock);
OSLockRelease(psCleanupData->hCleanupLock);

/* 原子操作用于简单计数 */
OSAtomicIncrement(&psCleanupData->iRef);
OSAtomicDecrement(&psCleanupData->iRef);
OSAtomicRead(&psMMUContext->sCacheFlags);

调试和诊断

MMU故障诊断:MMU_CheckFaultAddress

void MMU_CheckFaultAddress(MMU_CONTEXT *psMMUContext,
                           IMG_DEV_VIRTADDR *psDevVAddr,
                           MMU_FAULT_DATA *psOutFaultData)
{
    MMU_DEVICEATTRIBS *psDevAttrs = psMMUContext->psDevAttrs;
    MMU_LEVEL eMMULevel = psDevAttrs->psBaseConfig->ePxLevel;
    const MMU_PxE_CONFIG *psConfig;
    const MMU_DEVVADDR_CONFIG *psMMUDevVAddrConfig;
    IMG_HANDLE hPriv;
    MMU_Levelx_INFO *psLevel = NULL;
    PVRSRV_ERROR eError;
    IMG_UINT64 uiIndex;
    IMG_UINT32 ui32PCIndex = 0xFFFFFFFF;
    IMG_UINT32 ui32PDIndex = 0xFFFFFFFF;
    IMG_UINT32 ui32PTIndex = 0xFFFFFFFF;
    MMU_FAULT_DATA sMMUFaultData = {0};
    MMU_LEVEL_DATA *psMMULevelData;

    OSLockAcquire(psMMUContext->hLock);

    /* 假设4KB页面大小获取配置 */
    eError = psDevAttrs->pfnGetPageSizeConfiguration(12,
                                                     &psMMUPDEConfig,
                                                     &psMMUPTEConfig,
                                                     &psMMUDevVAddrConfig,
                                                     &hPriv);

    psLevel = &psMMUContext->sBaseLevelInfo;
    psConfig = psDevAttrs->psBaseConfig;

    sMMUFaultData.eTopLevel = psDevAttrs->psBaseConfig->ePxLevel;
    sMMUFaultData.eType = MMU_FAULT_TYPE_NON_PM;

    /* 遍历每一级页表 */
    for (; eMMULevel > MMU_LEVEL_0; eMMULevel--)
    {
        if (eMMULevel == MMU_LEVEL_3)
        {
            /* PC级别 */
            uiIndex = psDevVAddr->uiAddr & psDevVAddrConfig->uiPCIndexMask;
            uiIndex = uiIndex >> psDevVAddrConfig->uiPCIndexShift;
            ui32PCIndex = (IMG_UINT32)uiIndex;

            psMMULevelData = &sMMUFaultData.sLevelData[MMU_LEVEL_3];
            psMMULevelData->uiBytesPerEntry = psConfig->uiBytesPerEntry;
            psMMULevelData->ui32Index = ui32PCIndex;

            if (ui32PCIndex >= psLevel->ui32NumOfEntries)
            {
                psMMULevelData->ui32NumOfEntries = psLevel->ui32NumOfEntries;
                break;  /* 索引越界 */
            }

            /* 获取PCE数据 */
            (void)_MMU_GetPxEFaultLevelData(psMMUContext, psDevVAddr, psConfig,
                                            psLevel->sMemDesc.pvCpuVAddr,
                                            ui32PCIndex,
                                            psMMULevelData, NULL);

            psLevel = psLevel->apsNextLevel[ui32PCIndex];
            if (!psLevel)
            {
                break;  /* PC未分配 */
            }
            psConfig = psMMUPDEConfig;
            continue;
        }

        if (eMMULevel == MMU_LEVEL_2)
        {
            /* PD级别 */
            uiIndex = psDevVAddr->uiAddr & psDevVAddrConfig->uiPDIndexMask;
            uiIndex = uiIndex >> psDevVAddrConfig->uiPDIndexShift;
            ui32PDIndex = (IMG_UINT32)uiIndex;

            psMMULevelData = &sMMUFaultData.sLevelData[MMU_LEVEL_2];
            psMMULevelData->uiBytesPerEntry = psConfig->uiBytesPerEntry;
            psMMULevelData->ui32Index = ui32PDIndex;

            if (ui32PDIndex >= psLevel->ui32NumOfEntries)
            {
                psMMULevelData->ui32NumOfEntries = psLevel->ui32NumOfEntries;
                break;
            }

            /* 获取PDE数据和页大小 */
            (void)_MMU_GetPxEFaultLevelData(psMMUContext, psDevVAddr, psConfig,
                                            psLevel->sMemDesc.pvCpuVAddr,
                                            ui32PDIndex,
                                            psMMULevelData, &ui32Log2PageSize);

            /* 如果页大小不是4KB,重新获取配置 */
            if (ui32Log2PageSize != 12)
            {
                psDevAttrs->pfnPutPageSizeConfiguration(hPriv);
                eError = psDevAttrs->pfnGetPageSizeConfiguration(ui32Log2PageSize,
                                                                 ...);
            }

            psLevel = psLevel->apsNextLevel[ui32PDIndex];
            if (!psLevel)
            {
                break;  /* PD未分配 */
            }
            psConfig = psMMUPTEConfig;
            continue;
        }

        if (eMMULevel == MMU_LEVEL_1)
        {
            /* PT级别 */
            uiIndex = psDevVAddr->uiAddr & psMMUDevVAddrConfig->uiPTIndexMask;
            uiIndex = uiIndex >> psMMUDevVAddrConfig->uiPTIndexShift;
            ui32PTIndex = (IMG_UINT32)uiIndex;

            psMMULevelData = &sMMUFaultData.sLevelData[MMU_LEVEL_1];
            psMMULevelData->uiBytesPerEntry = psConfig->uiBytesPerEntry;
            psMMULevelData->ui32Index = ui32PTIndex;

            if (ui32PTIndex >= psLevel->ui32NumOfEntries)
            {
                psMMULevelData->ui32NumOfEntries = psLevel->ui32NumOfEntries;
                break;
            }

            /* 获取PTE数据 */
            (void)_MMU_GetPxEFaultLevelData(psMMUContext, psDevVAddr, psConfig,
                                            psLevel->sMemDesc.pvCpuVAddr,
                                            ui32PTIndex,
                                            psMMULevelData, NULL);
            break;
        }
    }

    psDevAttrs->pfnPutPageSizeConfiguration(hPriv);
    OSLockRelease(psMMUContext->hLock);

    *psOutFaultData = sMMUFaultData;
}

故障诊断输出示例

MMU Fault at Virtual Address: 0x0000_4020_1FFF
Level 3 (PC):
    Index: 2
    Entry: 0x0000_0008_0000_0003
    Status: valid
    Points to PD at: 0x8000_0000
Level 2 (PD):
Index: 2
    Entry: 0x0000_0008_0001_0003
    Status: valid
    Points to PT at: 0x8001_0000
Level 1 (PT):
    Index: 1
    Entry: 0x0000_0000_0000_0000
    Status: not valid  ← 故障原因:PTE无效
诊断:页表项未映射或已被释放

页表项故障数据获取

static IMG_BOOL _MMUGetPxEFaultLevelData(const MMU_CONTEXT *psMMUContext,
                                         IMG_DEV_VIRTADDR *psDevVAddr,
                                         const MMU_PxE_CONFIG *psConfig,
                                         void *pvCpuVAddr,
                                         IMG_UINT32 ui32PxIndex,
                                         MMU_LEVEL_DATA *psMMULevelDataOut,
                                         IMG_UINT32 *ui32Log2PageSizeOut)
{
    // 有效性字符串定义
    static const IMG_CHAR *apszMMUValidStr[4] = {
        "not valid",           // --
        "valid",              // -V
        "pending",            // P-
        "inconsistent"        // PV
    };

    if (psConfig->uiBytesPerEntry == 4) {
        IMG_UINT32 *pui32Ptr = pvCpuVAddr;
        psMMULevelDataOut->ui64Address = pui32Ptr[ui32PxIndex];

        // 检查有效性和挂起位
        if ((psConfig->uiPendingEnMask | psConfig->uiValidEnMask) <= 0xFFFFFFFF) {
            IMG_UINT32 uiValidBits = (((pui32Ptr[ui32PxIndex] & psConfig->uiPendingEnMask) != 0) << 1) |
                                     (((pui32Ptr[ui32PxIndex] & psConfig->uiValidEnMask) != 0) << 0);
            psMMULevelDataOut->psDebugStr = apszMMUValidStr[uiValidBits];
        } else {
            psMMULevelDataOut->psDebugStr = "";
        }

        // 获取页面大小
        if (ui32Log2PageSizeOut != NULL) {
            if (psDevAttrs->pfnGetPageSizeFromPDE4(pui32Ptr[ui32PxIndex], ui32Log2PageSizeOut) != PVRSRV_OK) {
                return IMG_FALSE;
            }
        }
    } else {
        IMG_UINT64 *pui64Ptr = pvCpuVAddr;
        psMMULevelDataOut->ui64Address = pui64Ptr[ui32PxIndex];

        // 计算有效性状态
        IMG_UINT32 uiValidBits = (((pui64Ptr[ui32PxIndex] & psConfig->uiPendingEnMask) != 0) << 1) |
                                 (((pui64Ptr[ui32PxIndex] & psConfig->uiValidEnMask) != 0) << 0);
        psMMULevelDataOut->psDebugStr = apszMMUValidStr[uiValidBits];

        // 获取页面大小
        if (ui32Log2PageSizeOut != NULL) {
            if (psDevAttrs->pfnGetPageSizeFromVirtAddr != NULL) {
                // MMU版本 >= 4的处理方式
                if (psDevAttrs->pfnGetPageSizeFromVirtAddr(psMMUContext->psPhysMemCtx->psDevNode, 
                                                           *psDevVAddr, ui32Log2PageSizeOut) != PVRSRV_OK) {
                    return IMG_FALSE;
                }
            } else if (psDevAttrs->pfnGetPageSizeFromPDE8(pui64Ptr[ui32PxIndex], ui32Log2PageSizeOut) != PVRSRV_OK) {
                // MMU版本 < 4的处理方式
                return IMG_FALSE;
            }
        }
    }

    return IMG_TRUE;
}

调试信息特点:
- 状态解析: 将页表项的位字段转换为可读状态
- 版本适配: 支持不同MMU版本的页面大小获取方式
- 完整信息: 提供地址和状态的完整描述

特殊功能和优化

虚拟地址有效性检查

IMG_BOOL MMU_IsVDevAddrValid(MMU_CONTEXT *psMMUContext,
                             IMG_UINT32 uiLog2PageSize,
                             IMG_DEV_VIRTADDR sDevVAddr)
{
    IMG_BOOL bStatus;
    const MMU_PxE_CONFIG *psConfig;
    IMG_HANDLE hPriv;
    const MMU_DEVVADDR_CONFIG *psDevVAddrConfig;

    _MMU_GetPTConfig(psMMUContext, uiLog2PageSize, &psConfig, &hPriv, &psDevVAddrConfig);

    MMU_GetVDevAddrPTE(psMMUContext, psConfig, psDevVAddrConfig, sDevVAddr, &bStatus);

    _MMU_PutPTConfig(psMMUContext, hPriv);

    return bStatus;
}

static IMG_UINT64 MMU_GetVDevAddrPTE(MMU_CONTEXT *psMMUContext,
                                     const MMU_PxE_CONFIG *psConfig,
                                     const MMU_DEVVADDR_CONFIG *psDevVAddrConfig,
                                     IMG_DEV_VIRTADDR sDevVAddr,
                                     IMG_BOOL *pbStatusOut)
{
    MMU_Levelx_INFO *psLevel = NULL;
    IMG_UINT32 uiIndex = 0;
    IMG_BOOL bStatus = IMG_FALSE;
    IMG_UINT64 ui64Entry = 0;

    OSLockAcquire(psMMUContext->hLock);

    switch (psMMUContext->psDevAttrs->psBaseConfig->ePxLevel) {
        case MMU_LEVEL_3:
            uiIndex = _CalcPCEIdx(sDevVAddr, psDevVAddrConfig, IMG_FALSE);
            psLevel = psMMUContext->sBaseLevelInfo.apsNextLevel[uiIndex];
            if (psLevel == NULL) break;
            __fallthrough;

        case MMU_LEVEL_2:
            uiIndex = _CalcPDEIdx(sDevVAddr, psDevVAddrConfig, IMG_FALSE);
            if (psLevel != NULL)
                psLevel = psLevel->apsNextLevel[uiIndex];
            else
                psLevel = psMMUContext->sBaseLevelInfo.apsNextLevel[uiIndex];
            if (psLevel == NULL) break;
            __fallthrough;

        case MMU_LEVEL_1:
            uiIndex = _CalcPTEIdx(sDevVAddr, psDevVAddrConfig, IMG_FALSE);
            if (psLevel == NULL)
                psLevel = &psMMUContext->sBaseLevelInfo;

            ui64Entry = ((IMG_UINT64 *)psLevel->sMemDesc.pvCpuVAddr)[uiIndex];
            bStatus = ui64Entry & psConfig->uiValidEnMask;
            break;

        default:
            PVR_LOG(("MMU_IsVDevAddrValid: Unsupported MMU setup"));
            break;
    }

    OSLockRelease(psMMUContext->hLock);

    *pbStatusOut = bStatus;
    return ui64Entry;
}

地址检查特点:
- 多级遍历: 完整遍历页表层次
- 早期退出: 任何级别失败都立即返回
- 原始数据: 同时返回状态和原始页表项值

后备页面管理

static PVRSRV_ERROR _MMU_AllocBackingPage(PVRSRV_DEVICE_NODE *psDevNode,
                                          IMG_INT uiInitValue,
                                          IMG_INT uiDefPage)
{
    PVRSRV_DEF_PAGE *psDefPage;
    IMG_CHAR *pcDefPageName;

    eError = _MMU_GetDefPage(psDevNode, uiDefPage, &psDefPage, &pcDefPageName);
    PVR_RETURN_IF_ERROR(eError);

    OSLockAcquire(psDefPage->psPgLock);

    if (psDefPage->ui64PgPhysAddr != MMU_BAD_PHYS_ADDR) {
        goto UnlockAndReturn;  // 已经分配过了
    }

    // 分配和初始化后备页面
    eError = DevPhysMemAlloc(psDevNode, (1 << psDefPage->ui32Log2PgSize), 0, uiInitValue, IMG_TRUE,
                             psDevNode->psMMUDevAttrs->pszMMUPxPDumpMemSpaceName,
                             pcDefPageName, &psDefPage->hPdumpPg,
                             PVR_SYS_ALLOC_PID, &psDefPage->sPageHandle, &sDevPAddr);

    psDefPage->ui64PgPhysAddr = sDevPAddr.uiAddr;

UnlockAndReturn:
    OSLockRelease(psDefPage->psPgLock);
    return eError;
}

static PVRSRV_ERROR _MMU_GetBackingPage(PVRSRV_DEVICE_NODE *psDevNode,
                                        IMG_UINT64 *pui64PgPhysAddr,
                                        IMG_INT uiDefPage)
{
    PVRSRV_DEF_PAGE *psDefPage;
    IMG_CHAR *pcDefPageName;

    eError = _MMU_GetDefPage(psDevNode, uiDefPage, &psDefPage, &pcDefPageName);
    PVR_RETURN_IF_ERROR(eError);

    OSLockAcquire(psDefPage->psPgLock);

    if (psDefPage->ui64PgPhysAddr == MMU_BAD_PHYS_ADDR) {
        eError = PVRSRV_ERROR_NOT_FOUND;
        goto UnlockAndReturn;
    }

    if (pui64PgPhysAddr)
        *pui64PgPhysAddr = psDefPage->ui64PgPhysAddr;

UnlockAndReturn:
    OSLockRelease(psDefPage->psPgLock);
    return eError;
}

后备页面特点:
- 单例模式: 每种后备页面在设备中只有一个实例
- 延迟分配: 只在实际需要时才分配
- 线程安全: 使用锁保护并发访问
- 初始化值: 支持不同的初始化值(零页面vs临时页面)

缓存管理接口

void MMU_AppendCacheFlags(MMU_CONTEXT *psMMUContext, IMG_UINT32 ui32AppendFlags)
{
    PVR_ASSERT(psMMUContext != NULL);
    if (psMMUContext == NULL) return;

    OSAtomicOr(&psMMUContext->sCacheFlags, (IMG_INT)ui32AppendFlags);
}

IMG_UINT32 MMU_GetAndResetCacheFlags(MMU_CONTEXT *psMMUContext)
{
    IMG_UINT32 uiFlags;

    PVR_ASSERT(psMMUContext != NULL);
    if (psMMUContext == NULL) return 0;

    uiFlags = (IMG_UINT32) OSAtomicExchange(&psMMUContext->sCacheFlags, 0);

#if defined(SUPPORT_PMR_DEFERRED_FREE)
    // 检查是否有需要清理的僵尸PMR
    if (PMRQueueZombiesForCleanup(psMMUContext->psPhysMemCtx->psDevNode)) {
        BITMASK_SET(uiFlags, RGXFWIF_MMUCACHEDATA_FLAGS_CTX_ALL | RGXFWIF_MMUCACHEDATA_FLAGS_PT);
    }
#endif

    return uiFlags;
}

缓存管理特点:
- 原子操作: 使用原子操作确保线程安全
- 累积标志: 支持多个标志的累积
- PMR集成: 与PMR延迟释放机制集成

设备初始化和清理

设备MMU初始化

PVRSRV_ERROR MMU_InitDevice(struct _PVRSRV_DEVICE_NODE_ *psDevNode)
{
    // 设置页面顺序为0(单页面)
    psDevNode->sScratchPage.sPageHandle.uiOrder = 0;
    psDevNode->sDevZeroPage.sPageHandle.uiOrder = 0;

    // 设置后备页面大小为最大页面大小
    if (psDevNode->ui32Non4KPageSizeLog2 != 0) {
        psDevNode->sScratchPage.ui32Log2PgSize = psDevNode->ui32Non4KPageSizeLog2;
        psDevNode->sDevZeroPage.ui32Log2PgSize = psDevNode->ui32Non4KPageSizeLog2;
    } else {
        psDevNode->sScratchPage.ui32Log2PgSize = OSGetPageShift();
        psDevNode->sDevZeroPage.ui32Log2PgSize = OSGetPageShift();
    }

    // 初始化物理地址为无效值
    psDevNode->sScratchPage.ui64PgPhysAddr = MMU_BAD_PHYS_ADDR;
    psDevNode->sDevZeroPage.ui64PgPhysAddr = MMU_BAD_PHYS_ADDR;

    // 创建后备页面锁(可能从MISR路径访问)
    eError = OSLockCreate(&psDevNode->sScratchPage.psPgLock);
    PVR_LOG_GOTO_IF_ERROR(eError, "OSLockCreate.Scratch", ErrReturnError);

    eError = OSLockCreate(&psDevNode->sDevZeroPage.psPgLock);
    PVR_LOG_GOTO_IF_ERROR(eError, "OSLockCreate.Zero", ErrFreeScratchPageLock);

    // 分配和初始化后备页面
    eError = _MMU_AllocBackingPage(psDevNode, PVR_SCRATCH_PAGE_INIT_VALUE, SCRATCH_PAGE);
    PVR_LOG_GOTO_IF_ERROR(eError, "_MMU_AllocBackingPage.Scratch", ErrFreeZeroPageLock);

    eError = _MMU_AllocBackingPage(psDevNode, PVR_ZERO_PAGE_INIT_VALUE, DEV_ZERO_PAGE);
    PVR_LOG_GOTO_IF_ERROR(eError, "_MMU_AllocBackingPage.Zero", ErrFreeScratchPage);

    return PVRSRV_OK;

ErrFreeScratchPage:
    _MMU_FreeBackingPage(psDevNode, SCRATCH_PAGE);
ErrFreeZeroPageLock:
    OSLockDestroy(psDevNode->sDevZeroPage.psPgLock);
    psDevNode->sDevZeroPage.psPgLock = NULL;
ErrFreeScratchPageLock:
    OSLockDestroy(psDevNode->sScratchPage.psPgLock);
    psDevNode->sScratchPage.psPgLock = NULL;
ErrReturnError:
    return eError;
}

设备初始化特点:
- 后备页面预分配: 在设备初始化时就分配好后备页面
- 页面大小适配: 根据设备能力选择最优的页面大小
- 错误恢复: 完整的资源清理路径
- MISR兼容: 锁设计考虑了中断处理路径的需求

设备MMU清理

void MMU_DeInitDevice(struct _PVRSRV_DEVICE_NODE_ *psDevNode)
{
    if (psDevNode->sScratchPage.psPgLock != NULL) {
        _MMU_FreeBackingPage(psDevNode, SCRATCH_PAGE);
        OSLockDestroy(psDevNode->sScratchPage.psPgLock);
        psDevNode->sScratchPage.psPgLock = NULL;
    }

    if (psDevNode->sDevZeroPage.psPgLock) {
        _MMU_FreeBackingPage(psDevNode, DEV_ZERO_PAGE);
        OSLockDestroy(psDevNode->sDevZeroPage.psPgLock);
        psDevNode->sDevZeroPage.psPgLock = NULL;
    }
}

清理特点:
- 条件检查: 防御性编程,检查指针有效性
- 资源配对: 与初始化操作严格配对
- 状态重置: 清理后重置指针为NULL

PDump集成和调试支持

PDump符号地址生成

PVRSRV_ERROR MMU_ContextDerivePCPDumpSymAddr(MMU_CONTEXT *psMMUContext,
                                             IMG_CHAR *pszPDumpSymbolicNameBuffer,
                                             size_t uiPDumpSymbolicNameBufferSize)
{
    size_t uiCount;
    IMG_UINT64 ui64PhysAddr;
    PVRSRV_DEVICE_IDENTIFIER *psDevId = &psMMUContext->psPhysMemCtx->psDevNode->sDevId;

    if (!psMMUContext->sBaseLevelInfo.sMemDesc.bValid) {
        return PVRSRV_ERROR_MMU_API_PROTOCOL_ERROR;
    }

    ui64PhysAddr = (IMG_UINT64)psMMUContext->sBaseLevelInfo.sMemDesc.sDevPAddr.uiAddr;

    // 生成符号名称格式: :device:MMUPC_<16位十六进制地址>
    uiCount = OSSNPrintf(pszPDumpSymbolicNameBuffer, uiPDumpSymbolicNameBufferSize,
                         ":%s:%s%016"IMG_UINT64_FMTSPECX,
                         psDevId->pszPDumpDevName,
                         psMMUContext->sBaseLevelInfo.sMemDesc.bValid ? "MMUPC_" : "XXX",
                         ui64PhysAddr);

    if (uiCount + 1 > uiPDumpSymbolicNameBufferSize) {
        return PVRSRV_ERROR_INVALID_PARAMS;
    }

    return PVRSRV_OK;
}

PDump符号特点:
- 唯一标识: 基于物理地址生成唯一符号
- 设备区分: 包含设备名称区分不同设备
- 格式标准: 遵循PDump工具的命名约定

PDump页目录基址写入

PVRSRV_ERROR MMU_PDumpWritePageCatBase(MMU_CONTEXT *psMMUContext,
                                       const IMG_CHAR *pszSpaceName,
                                       IMG_DEVMEM_OFFSET_T uiOffset,
                                       IMG_UINT32 ui32WordSize,
                                       IMG_UINT32 ui32AlignShift,
                                       IMG_UINT32 ui32Shift,
                                       PDUMP_FLAGS_T uiPdumpFlags)
{
    PVRSRV_ERROR eError;
    IMG_CHAR aszPageCatBaseSymbolicAddr[100];

    eError = MMU_ContextDerivePCPDumpSymAddr(psMMUContext,
                                             &aszPageCatBaseSymbolicAddr[0],
                                             sizeof(aszPageCatBaseSymbolicAddr));
    if (eError == PVRSRV_OK) {
        eError = PDumpWriteSymbAddress(psMMUContext->psPhysMemCtx->psDevNode,
                                       pszSpaceName, uiOffset,
                                       aszPageCatBaseSymbolicAddr, 0,
                                       psMMUContext->psDevAttrs->pszMMUPxPDumpMemSpaceName,
                                       ui32WordSize, ui32AlignShift, ui32Shift,
                                       uiPdumpFlags | PDUMP_FLAGS_CONTINUOUS);
    }

    return eError;
}

PDump写入特点:
- 符号引用: 使用符号地址而非直接地址
- 参数透传: 支持各种PDump写入参数
- 连续标志: 自动添加连续标志

PDump MMU上下文管理

PVRSRV_ERROR MMU_AcquirePDumpMMUContext(MMU_CONTEXT *psMMUContext,
                                        IMG_UINT32 *pui32PDumpMMUContextID,
                                        IMG_UINT32 ui32PDumpFlags)
{
    PVRSRV_DEVICE_IDENTIFIER *psDevId = &psMMUContext->psPhysMemCtx->psDevNode->sDevId;

    if (!psMMUContext->ui32PDumpContextIDRefCount) {
        PDUMP_MMU_ALLOC_MMUCONTEXT(psMMUContext->psPhysMemCtx->psDevNode,
                                   psDevId->pszPDumpDevName,
                                   psMMUContext->sBaseLevelInfo.sMemDesc.sDevPAddr,
                                   psMMUContext->psDevAttrs->eMMUType,
                                   &psMMUContext->uiPDumpContextID,
                                   ui32PDumpFlags);
    }

    psMMUContext->ui32PDumpContextIDRefCount++;
    *pui32PDumpMMUContextID = psMMUContext->uiPDumpContextID;

    return PVRSRV_OK;
}

PVRSRV_ERROR MMU_ReleasePDumpMMUContext(MMU_CONTEXT *psMMUContext,
                                        IMG_UINT32 ui32PDumpFlags)
{
    PVRSRV_DEVICE_IDENTIFIER *psDevId = &psMMUContext->psPhysMemCtx->psDevNode->sDevId;

    PVR_ASSERT(psMMUContext->ui32PDumpContextIDRefCount != 0);
    psMMUContext->ui32PDumpContextIDRefCount--;

    if (psMMUContext->ui32PDumpContextIDRefCount == 0) {
        PDUMP_MMU_FREE_MMUCONTEXT(psMMUContext->psPhysMemCtx->psDevNode,
                                  psDevId->pszPDumpDevName,
                                  psMMUContext->uiPDumpContextID,
                                  ui32PDumpFlags);
    }

    return PVRSRV_OK;
}

PDump上下文特点:
- 引用计数: 支持多个使用者共享PDump上下文
- 延迟分配: 只在首次获取时分配PDump上下文
- 资源配对: 获取和释放严格配对

架构特点总结

设计原则

  1. 分层架构:
    • 应用层 → devicemem → mmu_common → pmr/device
    • 清晰的职责分离和接口抽象
  2. 资源管理:
    • 引用计数确保资源生命周期正确性
    • 延迟释放解决缓存一致性问题
    • RA集成提供灵活的物理内存管理
  3. 并发安全:
    • 多级锁保护不同粒度的数据
    • 原子操作用于高频访问的标志
    • 清理线程避免阻塞主路径
  4. 性能优化:
    • 快速映射路径减少函数调用开销
    • 批量操作减少系统调用
    • 配置缓存避免重复计算
  5. 错误处理:
    • 完整的错误恢复路径
    • 防御性编程防止崩溃
    • 详细的调试信息支持

关键技术

  1. 多级页表管理:
    • 通用的递归结构支持不同MMU架构
    • 按需分配节省内存
    • 智能引用计数管理
  2. 稀疏内存支持:
    • 后备页面机制
    • 零页面和临时页面的智能选择
    • 动态映射/解映射
  3. 虚拟化和安全:
    • OSID隔离支持多OS环境
    • 客户机/主机模式支持
    • 可信设备集成
  4. 调试和分析:
    • 完整的PDump集成
    • 详细的故障分析能力
    • HTB性能日志记录

代码质量特点

  1. 可维护性:
    • 清晰的命名约定
    • 充分的注释说明
    • 模块化的函数设计
  2. 可移植性:
    • OS抽象层使用
    • 设备特定功能通过函数指针
    • 条件编译支持不同配置
  3. 鲁棒性:
    • 参数验证
    • 资源泄漏检测
    • 异常情况处理

页表项的硬件特定实现

不同GPU架构的页表项格式会有差异。从代码中可以看出,通过设备特定的函数指针来处理这些差异:

// PowerVR可能的页表项格式
pfnDerivePTEProt8(uiProtFlags, uiLog2DataPageSize)

这种设计允许同一套MMU代码支持不同的GPU架构,每个架构只需要实现自己的保护位计算函数。
1. 奇偶校验机制的重要性
代码中的奇偶校验不仅仅是数据完整性检查,更重要的是:

  1. 硬件错误检测: GPU访问页表时如果发生位翻转,可以被检测出来
  2. 调试支持: 帮助定位内存传输问题
  3. 可靠性提升: 在高频GPU操作中确保页表项的正确性

  4. 延迟释放的深层原因
    延迟释放机制解决的核心问题是GPU的异步特性:

  5. 流水线深度: GPU有深度的指令流水线,页表访问可能有延迟

  6. 缓存层次: 多级缓存需要时间来失效
  7. 并发访问: 多个GPU单元可能同时访问同一页表

这种设计确保在页表被实际释放前,所有可能的访问都已经完成。
3. 虚拟化支持的考虑
代码中的OSID(Operating System ID)支持体现了现代GPU在虚拟化环境中的应用:

  1. 多OS隔离: 在虚拟机环境中隔离不同操作系统的内存访问
  2. 安全性: 防止一个虚拟机访问另一个虚拟机的内存
  3. 性能: 避免频繁的页表切换开销

这个MMU框架的设计充分体现了现代GPU系统的复杂性需求,在性能、可靠性、安全性和可维护性之间找到了平衡点。
这个MMU管理系统代表了现代GPU驱动程序的复杂性和精密性,充分考虑了性能、安全性、调试能力和维护性等多个方面的需求。

相关文章

PowerVR RGX Sync-Fence
PowerVR Rogue ZS Buffer
PowerVR RGX Parameter Buffer And Ring Buffer
PowerVR Rogue FreeList
PowerVR RGX Firmware Utils
PowerVR RGX BVNC

发布评论