虚拟内存是现代计算机体系结构的核心机制之一,它通过地址转换将程序使用的虚拟地址(VA)映射到实际的物理地址(PA)。AArch64架构作为ARMv8和ARMv9指令集的重要组成部分,其虚拟内存管理系统设计精巧且功能强大。
在AArch64中,虚拟地址到物理地址的转换通常涉及两个阶段:
这种两阶段转换机制为虚拟化提供了硬件支持,允许hypervisor管理多个操作系统的内存空间。每个阶段都使用独立的多级页表结构,支持4KB、16KB和64KB等多种页大小配置。
AArch64采用多级页表结构实现地址转换,页表项主要分为三种类型:
表描述符(Table Descriptor)
code复制[63:12]:下一级页表基地址
[11:2]:保留
[1]:类型标识(1表示有效项)
[0]:有效位
块描述符(Block Descriptor)
code复制[47:30]:输出地址的高位
[29:12]:内存属性控制
[11:2]:保留
[1]:类型标识(0表示块描述符)
[0]:有效位
页描述符(Page Descriptor)
地址转换的核心流程在伪代码函数AArch64_TranslateAddress中体现:
c复制func AArch64_TranslateAddress(va, accdesc, aligned, size) {
if (特殊计数器采样中) {
启动性能计数器();
}
// 完整地址转换
var result = AArch64_FullTranslate(va, size, accdesc, aligned);
if (!IsFault(result) && 不是指令获取) {
// 检查调试相关权限
result.fault = AArch64_CheckDebug(va, accdesc, size);
}
if (支持RME扩展) {
// 执行颗粒保护检查
result.fault.gpcf = GranuleProtectionCheck(result, accdesc);
}
if (特殊计数器采样中) {
停止性能计数器();
}
// 更新虚拟地址信息
result.vaddress = ZeroExtend(va);
return result;
}
阶段1遍历的核心函数是AArch64_S1Walk,其主要流程如下:
初始化遍历状态
c复制var walkstate = AArch64_S1InitialTTWState(walkparams, va, regime, ss);
层级遍历循环
c复制repeat {
// 获取当前层级的页表项
(fault, descriptor) = FetchDescriptor(ee, walkaddress, walkaccess, fault);
// 解析描述符类型
desctype = AArch64_DecodeDescriptorType(descriptor, walkparams);
switch(desctype) {
case Table:
// 准备下一级遍历
walkstate = AArch64_S1NextWalkStateTable(...);
break;
case Leaf:
// 找到最终映射
walkstate = AArch64_S1NextWalkStateLeaf(...);
break;
case Invalid:
// 触发页错误
fault.statuscode = Fault_Translation;
break;
}
} until 找到最终映射或出错;
后处理检查
阶段2遍历与阶段1类似,但有一些关键差异:
初始化不同
c复制if (安全状态) {
walkstate = AArch64_SS2InitialTTWState(walkparams, ipaspace);
} else {
walkstate = AArch64_S2InitialTTWState(ss, walkparams);
}
属性处理差异
输出地址空间处理
c复制if (安全状态) {
baseaddress.paspace = AArch64_SS2OutputPASpace(walkparams, ipaspace);
}
权限检查贯穿整个地址转换过程,主要包括:
访问标志(AF)检查
c复制if (descriptor[10] == '0' && walkparams.ha == '0') {
fault.statuscode = Fault_AccessFlag;
}
权限位检查
特殊权限检查
c复制if (支持颗粒保护) {
result.fault.gpcf = GranuleProtectionCheck(result, accdesc);
}
内存属性控制内存访问行为,主要包括:
属性索引解析
c复制if (walkparams.aie == '1') {
attrindx = descriptor[5:2]; // 扩展属性索引
} else {
attrindx = '0'::descriptor[4:2];
}
MAIR寄存器映射
c复制attr = AArch64_MAIRAttr(UInt(attrindx), walkparams.mair2, walkparams.mair);
最终属性解码
c复制memattrs = S1DecodeMemAttrs(attr, sh, s1aarch64, walkparams, accdesc.acctype);
AArch64支持内存标签扩展(MTE),相关转换函数:
c复制func AArch64_TranslateTagAddress(va, accdesc_in, aligned, size) {
accdesc.datafortagaccess = TRUE;
daddrdesc = AArch64_TranslateAddress(va, accdesc, aligned, size);
if (daddrdesc.memattrs.tags == MemTag_AllocationTagged) {
tva = S1VirtualTagAddress(va, accdesc.el, accdesc.ss);
taddrdesc = AArch64_TranslateAddress(tva, taccdesc, taligned, tsize);
return (daddrdesc.memattrs.tags, taddrdesc);
}
}
连续位(Contiguous bit)支持大块内存映射优化:
c复制func AArch64_ContiguousBit(tgx, d128, level, descriptor) {
if (特殊配置要求忽略连续位) {
return '0';
}
// 不同页大小配置下的连续位位置
if (d128 == '1') {
return descriptor[111];
} else {
return descriptor[52];
}
}
RME扩展引入的颗粒保护检查:
c复制if (IsFeatureImplemented(FEAT_RME)) {
result.fault.gpcf = GranuleProtectionCheck(result, accdesc);
if (result.fault.gpcf.gpf != GPCF_None) {
result.fault.statuscode = Fault_GPCFOnOutput;
}
}
ASID和VMID使用
预取优化
c复制if (SPESampleInFlight) {
SPEStartCounter(SPECounterPosTranslationLatency);
}
起始层级由地址范围和配置决定:
c复制func AArch64_S1StartLevel(walkparams) {
if (VARange == LOWER && walkparams.t0sz > walkparams.t1sz) {
return 0; // 从第0级开始
} else {
return 1; // 从第1级开始
}
}
系统可同时支持不同大小的页:
c复制func AArch64_BlockDescSupported(d128, ds, tgx, level) {
case tgx of
when TGx_4KB:
return ((level == 0 && (ds == '1' || d128 == '1')) || level == 1 || level == 2);
when TGx_16KB:
return ((level == 1 && (ds == '1' || d128 == '1')) || level == 2);
when TGx_64KB:
return ((level == 1 && (d128 == '1' || PA最大位数 >= 52)) || level == 2);
end;
}
Fault_Translation
Fault_AccessFlag
Fault_AddressSize
页表遍历调试
AArch64_S1Walk和AArch64_S2Walk伪代码作为参考权限问题排查
性能问题分析
Linux内核中AArch64页表处理的关键函数:
__create_page_tables:初始化页表__cpu_setup:配置MMU寄存器pgd_offset等宏:页表遍历KVM中阶段2转换的处理:
kvm_init_stage2_mmu:初始化阶段2MMUkvm_stage2_map:处理IPA到PA的映射资源受限系统的优化策略:
更大的地址空间
增强的安全特性
性能持续优化
页大小选择
权限配置
性能调优
调试技巧