在宏伟的ARM的内存世界中VMSA中,属性这个议题算不上最亮的星,就和屏幕前的你和我一样,平凡的活在这个茫茫然的人世间。纵使“丈夫贫践应未足,今日相逢无酒钱。”,也不要灰心面对生活,因为“山重水复疑无路,柳暗花明又一村”。这个时代磨炼着每一个人,考验着每一个人的意志力,只要坚持走下去,“莫愁前路无知己,天下谁人不识君”。本文成文的早晨,发生了一些遗憾的事情,谨以此文激励一下每一个屏幕前奋斗的码农,每一个为家庭默默付出的人。内存的属性虽然平凡,但是仍然足够重要,因为它们是构成这个VMSA系统一些特征的基石,前面的文章我们也穿插讨论了一些,例如内存的类型、内存的共享属性等等,本文我们会对其他一些内存属性进行讨论。内存的属性其实不太准确,准确的说法应该是内存空间的属性。前面的文章我们已经了解到了,CPU是工作在虚拟地址空间的,它要访问物理地址空间的时候需要依赖MMU做一下翻译的工作。而整个的SOC系统也是通过内存空间组织在一起的,不管是数据的交互、还是CPU指令的发出都要依赖内存空间。内存空间基本分类就是device和Normal,它们就工作在不同的业务场景下。直观的体现就是表现出来的行为就不一样,一个外设的寄存器会被映射在设备类型空间,那么它肯定是不希望被cache的,因为它的值随时都可能被改变,但是它又没有好的机制保证和CPU看到的数据的一致性。Normal类型的数据为了能够减少访问外存的时钟周期,多数是要被缓存的,而且CPU在出厂的时候,会有完整的Cache一致性机制和替换策略保证数据的一致性。类似种种情况,结合ARM的架构,都需要对内存空间做进一步的细分,应用层的OS或者Firmware在申请内存的时候,顺便把内存的属性填充好,这样CPU工作的时候让小弟遍历这些属性就能准确的控制整个系统的行为。检查通过,就继续工作,检查失败就会抛出异常,启动异常处理机制进行“惩罚”。既然属性是内存空间的属性,那么首先就要对内存空间有起码的了解。ARM的体系下构建了一个虚拟的内存世界:AArch64 Virtual Memory System Architecture (VMSA)。顾名思义,ARM的内存世界是一个虚拟的世界,ARM通过编译器所有在上面运行的软件都编上了一个统一的地址。软件代码在编译和执行的过程中,看到的也都是虚拟的地址。当CPU拿到一个虚拟地址后,他需要做的一件事情就是要把这个虚拟的地址VA分配一个真实的物理PA地址与之对应,这样整个VMSA架构体系就运转了起来。随着操作系统的发展,以及在工程实践中的经验积累,人们发现计算机的世界需要等级、需要调控。不然就会产生混乱,比如系统上就一个喇叭能发声,A应用想发声,B应用也想发声,怎么办?得有更高权限的模块处理这些事务,并定下规则,于是ARM的异常模型诞生了,如图1-1所示。图1-1 ARM异常等级
异常的等级越高,权限越大,其实就是控制资源的权限越高。计算机的世界在有了秩序之后玩命的卷了起来,不管是虚拟化技术、操作系统技术、还有各种各样的应用技术都在飞快的发展。伴随着欣欣向荣的景象,另外一些需求也随之产生,那就是安全。和现实的世界一样,总有一些心怀叵测的人通过各种手段窥伺别人的隐私信息,比如一个公钥、一个证书、或者一个生物(指纹)信息。安全无小事,ARM架构在迭代的过程中也引入了安全状态,也开始了对ARM世界的第二次跑马圈地。图1-2 安全状态
“星星还是那个星星,月亮还是那个月亮”,CPU还是那个CPU,内存还是那个内存。如果这个世界一直就是这样的话,怎么能表现Non-Secure安全状态下Hypervisor的地位、怎么表现出Secure状态下Trusted OS的身份....。ARM的架构下就是靠内存世界的划分加以区别,也就是CPU还是那个CPU,但是内存却不是那个内存了,因为ARM把虚拟内存的空间做了细分。The architecture defines all of the following translation regimes:• Non-secure EL1&0 translation regime.• Secure EL1&0 translation regime.• Realm EL1&0 translation regime.• Non-secure EL2&0 translation regime.• Secure EL2&0 translation regime.• Realm EL2&0 translation regime.• Non-secure EL2 translation regime.• Secure EL2 translation regime.• Realm EL2 translation regime.• EL3 translation regime.
可以通过PSATE家族的CurrentEL寄存器锁定EL状态(如图1-3所示):图1-3 CurrentEL寄存器
同样,可以通过PSTATE家族的SCR_EL3寄存器锁定Security状态(如图1-4、图1-5所示):图1-4 SCR_EL3寄存器
图1-5 SCR_EL3寄存器Secure状态
有了上面的机制后,CPU上电后经过引导程序的加载,各个空间的软件逐渐被加载进内存并开始执行,这个时候要做的事情有很多,其中最重要的一项就是初始化自己的虚拟地址空间。比如一个Linux系统要执行,她就需要给自己找一块内存空间,比如None-Secure EL1&0,这个时候的画面就变成了这个样子如图1-6所示。图1-6 High-Level内存映射
Linux在运行时,它跑在一个虚拟的地址空间内,但实际上它要加载指令、加载数据、操作外部设备都要用到物理地址空间,如同美国打伊拉克也得使用GPS定位数据,而不能只告诉导弹一个村子的名字。这个时候,情况就变得复杂起来了,虚拟空间太大了(0x16个0到0x16个F),物理空间不可能这么大,这得多大一个内存条啊。那么物理内存空间只能先进行切片分割然后分时和虚拟地址空间相应切片区域做映射,在一个时间点上,一段物理内存装的也许是指令,在另外一个时间点上可能装的就是被操作的数据了。这么复杂的局面下,为了保证OS在运行的过程中不能出错,比如分配给A进程的空间,就不能再分配给B进程了,此时就需要有一个“账本”把这一笔一笔的记录清楚,用我们历史上第一金牌律师海瑞大人的话说:记录在案。那么,此时图1-6,就变成了图1-7的局面。图1-7 High-Level内存映射(页表)
账本都有了,是不是就可以高枕无忧了?还是不行,因为这个账本也需要存在内存里面,也是有开销的,即便是划片映射,这个开销也是很大,如果只搞一级映射会浪费很多内存资源,这都是钱啊。于是映射的过程继续进化变成了图1-8这个样子。图1-8 High-Level内存映射(多级页表)
关于多级页表架构以及多级页表架构为什么可以节约内存开销,我们前序文章有专门的论述:[A-11]ARMv8/ARMv9-Memory-多级页表架构。有了多级页表架构的加持,软件就可以从容的使用虚拟地址了,每当CPU发出一个虚拟地址,CPU的小弟就会在操作系统的配合下,高低给这个VA映射到一个PA上,这个过程如图1-9所示。图1-9 虚拟地址翻译过程
图1-10 ARM多级页表架构
VNSA体系下,VA和PA就是靠D_Table、D_Block、D_Page实现的级联架构,这些账本内部结构如图1-11所示:图1-11 High-Level页表描述符
前面的小结,我们知道了内存的属性在页表中被描述,页表的基本各个被成为页表描述符,那内存的属性自然也在页表描述符中占据一席之地,如图1-12所示。图1-12 Block 描述符
我们对图1-12的Block描述放大就可以看到它内部更清晰的结构,其他描述符也是类似结构我们就不一一展开了,下面的章节我们就循着页表描述符的足迹对这些重要的内存属性做详细的介绍。(1)内存的类型内存分为两种类型,Device和Normal,如图1-13所示.图1-13 内存的类型
上图是一般内存空间(EL1&0)的内存配置方式,OS在编译期间就要在相关的配置文件中对物理地址空间做好划分,等待着相应类型的虚拟地址空间进行映射。(2)内存的Cache属性决定了对应的内存片段是否可以被缓存到CPU的内部Memory子系统中(L1/L2/L3 Cache)中,关于Cache可以参考前序的Cache系列文章。和大多数存储系统的访问动作一样,对内存的访问也是“读”和“写”。ARM使用页表描述符中的“AP”对访问做了权限的限制,如图1-14所示。图1-14 内存访问权限配置
一般情况下,ARM体系下高Level权限下运行的软件可以无条件的访问低Level权限内存空间,如手册中的描述:The standard permission model is that a more privileged entity can access anything belonging to a less privileged entity. For example, an Operating System (OS) can see all the resources that are allocated to an application, or a hypervisor can see all the resources that are allocated to a virtual machine (VM). This is because executing at a higher exception level means that the level of privilege is also higher.
这种权限的模型设计,一般情况下是没问题的,但是有的时候比如编码错误或者系统漏洞等情况下的软件攻击,也会对低级别的软件内存空间带来危害和风险,因此ARM也有预防措施比如通过系统寄存器进行限制,如图1-15所示。图1-15 访问权限的限制
除了通过系统寄存器的配置进行控制外,还可以通过指令进行控制,这一部分我们不展开讨论,详细的细节,小伙伴们可以查阅手册。可执行属性比较好理解,那就是这部分内存空间存储的数据是否可以被当成指令被CPU加载从而被执行,ARM通过“UXN”和“PXN”这两个字段描述。内存空间分成两种类型:Device和Normal,我们简单的展开讨论一下。(1) 当一个系统软件分配内存的时候把一段内存空间赋予一个Device属性的时候,那么大多数情况下OS只是想通过这段通过这段内存空间完成的外部设备的控制,比如通知一个DMA控制器,从哪里开始拷贝内存中数据。那么这个时候,通常就不需要Device类型的内存空间存储可执行的指令。(2) Normal类型的内存空间不但要存储指令,还要存储指令要操作的数据,比如图片的编码,那么存储数据的这一段内存也不需要被赋予可执行权限。基于以上分析,我们来看一个典型的EL1&0内存空间的可执行权限配置,如图1-16所示。图1-16 可执行权限的配置
访问标志(Access Flag)就比较好理解了。ARM内存分配的颗粒度是页(4k、16k、64k)和块,ARM用这个标志来表明当前分配的这个内存区域是否已经被访问过了。You can set the AF bit to:• AF=0. Region not accessed.• AF=1. Region accessed.
实际使用场景中,这个标志位主要被OS利用(也可以通过配置系统寄存器TCR_ELx.HA进行硬件设置)做内存性能优化的重要参考标志位,比如内存空间紧张时候的Swap操作上下文等,如图1-17所示。图1-17 经典换出算法-LRU
熟悉Linux系统的小伙伴应该知道,Linux分配一个页分为两种情况“文件页”和“匿名页”,当我们打开一个可编辑的文件页的时候,例如一个txt格式的文件,那么这个文件的内容就会被加载进“文件页”,当这个文件页被修改之后,ARM同样会在页表中做一个记录,只是对这个状态的记录稍微复杂一些因为它关联了页表描述符中的两个Bit,以Stage-1的Page描述为例,如图1-18所示。图1-18 Stage-1页表描述符
The dirty state is used to indicate a memory block or page has been modified. When hardware update of the dirty state is enabled, the descriptor DBM field indicates whether the descriptor is a candidate for hardware updates of the dirty state.在DBM的配置下,Dirty的状态同样支持硬件配置,这里不展开讨论。而Dirty State也非常简单,如图1-19所示。图1-19 Dirty State
需要注意的是关于DBM这个Bit的解读,很多大神的观点是有待商榷的。大家可以自行阅读手册进行判断,但是瑕不掩瑜,小小的偏差不能掩盖大神的伟大。内存的核心属性,我们基本都做了介绍,还有一些常用属性限于篇幅也不展开了,大家可以自行阅读手册或者参考如下快查表,如图1-20所示(除了个别属性外,大多数属性的介绍可以直接使用,亲测有效,哈哈哈)。图1-20 ARM内存属性快查表
ARM的TLB中也会缓存一部分内存属性,这个也是个挺有意思的点对于研究ARM架构来说。具体来说,TLB(Translation Lookaside Buffer,转译后备缓冲器)是处理器内存管理单元(MMU)的一部分,用于加速虚拟地址到物理地址的转换过程。在ARM架构中,TLB条目不仅包含虚拟地址和物理地址的映射关系,还包含诸如内存类型、缓存策略、访问权限、地址空间ID(ASID)和虚拟机ID(VMID)等属性,如图1-21。图1-21 典型的TLB结构
TLB究竟能缓存多少内存属性,也要看具体的CPU微架构的设计,以及芯片厂商的实现,如Cortex-A725的TLB结构。这里我们只引用部分手册的描述,也不展开讨论了。Translation Lookaside Buffer (TLB) entries store the context information required to facilitate a match and avoid the need for a TLB clean on a context or virtual machine switch.• A Physical Address (PA)• A set of memory properties that includes type and access permissionsEach TLB entry is associated with either:• A particular Address Space IDentifier (ASID)Each TLB entry also contains a field to store the Virtual Machine IDentifier (VMID) in the entry applicable to accesses from EL0 and EL1. The VMID permits hypervisor virtual machine switches without requiring the TLB to be invalidated.
本文我们详细地介绍了内存空间的背景知识,之后又介绍了内存空间大部分的属性。本文内容稍多,虽然已经压缩了很多内容,但是还是稍显繁具,不过还是希望对家能够有所启发。随着ARM架构的迭代,关于内存属性还有更高级的用法也值得研究,这里大家先掌握基础知识体系,高级的用法后面我们也会择机给大家进行介绍。另外,由于多级页表架构下每一级的页表描述符OS都可以进行属性设置,对与上下级页表中重叠的页表熟悉,也有覆盖关系,限于篇幅我们也不讨论了,算是给各位读者留课后作业了。最后,就是虚拟技术的引入,内存的两级翻译架构,Stage-2阶段的内存分配也有自己的属性,而且两级属性同样有覆盖规则,这些内容我们后面已经规划了专门的文章进行介绍。开头的时候略显伤感一点,确实有些事情真的不希望看到。真心希望每个码农和家人都能开开心心的度过每一天,谢谢大家,请保持关注。[00] <corelink_dmc520_technical_reference_manual_en.pdf>[01] <corelink_dmc620_dynamic_memory_controller_trm.pdf>[02] <IP-Controller/DDI0331G_dmc340_r4p0_trm.pdf>[03] <80-ARM-IP-cs0001_ARMv8基础篇-400系列控制器IP.pdf>[04] <arm_cortex_a725_core_trm_107652_0001_04_en.pdf>[05] <DDI0487K_a_a-profile_architecture_reference_manual.pdf>[06] <armv8_a_address_translation.pdf>[07] <cortex_a55_trm_100442_0200_02_en.pdf>[08] <learn_the_architecture_aarch64_memory_management_guide_en.pdf>[09] <learn_the_architecture_armv8-a_memory_systems_en.pdf>[10] <79-LX-LK-z0002_奔跑吧Linux内核-V-2-卷1_基础架构.pdf>[11] <79-LX-LD-s003-Linux设备驱动开发详解4_0内核-3rd.pdf>[12] <learn_the_architecture_memory_systems_ordering_and_barriers.pdf>[13] <arm_dsu_120_trm_102547_0201_07_en.pdf>[14] <80-ARM-MM-AL0001_内存学习(三):物理地址空间.pdf>[15] <learn_the_architecture_aarch64_memory_attributes_and_properties.pdf>
MMU - Memory Management UnitTLB - translation lookaside bufferVIPT - Virtual Index Physical TagVIVT - Virtual Index Virtual TagPIPT - Physical Index Physical TagIPS - Intermediate Physical SpaceIPA - Intermediate Physical AddressVMID - virtual machine identifierTLB - translation lookaside buffer(地址变换高速缓存)VTTBR_EL2 - Virtualization Translation Table Base RegistersASID - Address Space Identifier (ASID)DMC - Dynamic Memory ControllerDDR SDRAM - Double Data Rate Synchronous Dynamic Random Access MemoryDMB - Data Memory BarrierDSB - Data Synchronization Barrier ISB - Instruction Synchronization BarrierDSU - DynamIQ ™ Shared UnitVMSA - AArch64 Virtual Memory System Architecture