[A-24][V-09]ARMv8/v9-SMMU工作场景与SMMU的虚拟化架构

ver0.1

[看前序文章有惊喜,关注W\X\G=Z+H=“浩瀚架构师”,可以解锁全部文章]

前言

我们在介绍ARM的内存体系的时候,行文中经常讲MMU比作PE-Cores的带刀护卫。按照这个逻辑,那么SMMU也可以称之为总线上各个Master(设备)的带刀护卫,利刃出鞘之后,任何驱动送过来的地址都会被翻译成正确的物理地址、内存中数据也会得到相应的保护、共享SMMU的各个设备也会和睦相处,任何不安守本分的行为都会被惩罚,抛出异常让CPU这个总司令去处理。虽然大家同为带刀护卫,但是工作的场景还是有所区别的,前面我们虽然做了一些介绍,但是偏于原理性质的居多。本文我们会结合具体上下文,讲述一下SMMU在不同的阶段是如何工作的,以及这些工作的意义。最后我们打算花一点时间,简要介绍一下独立形态工作的SMMU的虚拟化架构。俗话说谁还不是个宝宝,就算是好兄弟也是有所区别的,希望大家对SMMU这个设备架构了解之后,能建立起对SMMU正确的认知,毕竟人和人还是不一样的。

同样的道理,SMMU的相关背景知识,我们已经写了三篇文章做了介绍。还没有看过的同学,建议大家不着急越读本文,先去看看前序的文章,建立起对SMMU的感觉。
(1) [A-21]ARMv8/v9-SMMU系统架构和功能概述
(2) [A-22]ARMv8/v9-SMMU多级页表架构
(3) [A-23]ARMv8/v9-SMMU-设备虚拟地址翻译(设备页表映射)

正文

前文中已经介绍了SMMU的工作模式,SMMU是支持两级地址翻译的,但是经过Bypass的配置可以实现多种组合的工作方式。下面我们先进入SMMU只工作在Stage-1阶段模式的场景,在这个基础上再介绍SMMU工作在Stage-2阶段模式的场景。

1.1 Stage-1 USE Case(Linux Kernel中的SMMU)

我们结合Linux系统来说明SMMU的在Stage-1阶段的用法。原因是Linux是宏内核且开源,EL0/El1空间界限相对清晰,帮助文档比较完善,而且都是业界的顶级公司和大牛在做长期维护,质量和水平那是绝对值得信赖的。看一下Kernel中一个比较经典的例子,如图1-1所示。
1-1

图1-1 High-Level DMA ARCH IN Kernel

Kernel的Document中对这个例子有如下的描述:

• During the enumeration process, the kernel learns about I/O devices and their MMIO space and the host bridges that connect them to the system. For example, if a PCI device has a BAR, the kernel reads the bus address (A) from the BAR and converts it to a CPU physical address (B). The address B is stored in a struct resource and usually exposed via /proc/iomem. When a driver claims a device, it typically uses ioremap() to map physical address B at a virtual address ©. It can then use, e.g., ioread32©, to access the device registers at bus address A.

• If the device supports DMA, the driver sets up a buffer using kmalloc() or a similar interface, which returns a virtual address (X). The virtual memory system maps X to a physical address (Y) in system RAM. The driver can use virtual address X to access the buffer, but the device itself cannot because DMA doesn’t go through the CPU virtual memory system.

• In some simple systems, the device can do DMA directly to physical address Y. But in many others, there is IOMMU hardware that translates DMA addresses to physical addresses, e.g., it translates Z to Y. This is part of the reason for the DMA API: the driver can give a virtual address X to an interface like dma_map_single(), which sets up any required IOMMU mapping and returns the DMA address Z. The driver then tells the device to do DMA to Z, and the IOMMU maps it to the buffer at address Y in system RAM.

• So that Linux can use the dynamic DMA mapping, it needs some help from the drivers, namely it has to take into account that DMA addresses should be mapped only for the time they are actually used and unmapped after the DMA transfer.

我们来解读一下这段描述:
(1) 首先是MMIO Space,前面写过一篇文章专门讲了ARM内存空间的类型,[A-14]ARMv8/ARMv9-Memory-内存模型的类型(Device & Normal) 。精读过那篇文章的小伙伴伴儿们,理解起来一点都不陌生。CPU要控制SOC上的设备总要有一个渠道发出自己的指令,这个渠道的地址提前SOC厂商都会分配好的。这一段给GPU用,那一段给NPU用,那一段给ADSP用…这些地址空间在VMSA的体系下被称之为Device类型,如图1-2所示。总之,CPU要控制这个设备就要使用这段地址空间,进行操作。当然,CPU只能通过虚拟地址访问内存空间,MMU会帮助它做相应的转换,把VA变成PA,最后把驱动要传递的数据输送设备的寄存器上。那么在总线架构上,DMA控制器也是一个设备,也受到运行在CPU上的寄存器的管理。当CPU因为某种原因,其他设备需要借助DMA控制器传输数据的时候,那个需要帮忙的设备的驱动程序就会通过DMA驱动架构提供的接口,将需要传递的数据的地址传递给DMA控制器。在本例中,这个地址就是X,它是一个虚拟地址。
1-2

图1-2 ARM内存空间的分类

(2) X这个虚拟地址对应的内存类型可是实实在在的RAM,如果要向外发送数据,那么CPU就要通过X映射的物理地址Y访问这段内存,并做好相应的生产动作。搞完这一切之后,就可以发一个信号给需要传递数据设备(DMA),这个设备就会拿着X对应的总线地址Z(其实就是X,这个和具体的实现体系架构有关,这里我们默认在VMSA体系下且SMMU和MMU公用页表的前提下讨论),并且把这个Z传递给IOMMU(SMMU)。SMMU拿着这个Z到页表中遍历一圈,最后返回一个物理地址Y给DMA。DMA拿着这个Y就可以开始干活了,消费掉CPU之前在X(Y)处生成的数据。那么这个过程反之亦然。

我们看一个具象化的例子,帮助大家进一步理解,如图1-3所示。
1-3

图1-3 High-Level Android Audio ARCH

我们对这个例子做一下总结(大家了解原理框架即可,细节不一定严谨,具体要看设备驱动的实现)
(1) Audio的驱动申请了一块Buffer(DMA-Buffer)用于生产数据。
(2) 这块儿Buffer会通DMA的接口映射到DMA控制器的地址空间。
(3) DMA的驱动会调用SMMU的驱动去初始化SMMU的相应数据结构(STE/CD)。
(4) DMA的驱动接受Audio驱动请求开始传递数据时,它会把DMABuffer的首地址VA传递给DMA-Controller。
(5) DMA-Controller拿着VA/StreamID等信息找到SMMU翻译成物理地址PA。
(6) DMA-Controller就开始从DMA-Buffer中搬运(消费)数据到AFE。

通过这个例子,也许大家会感到困惑,费这个劲干啥,直接把DMA-Buffer的物理首地址告诉DMA-Controller让它搬运不就行了吗?也可以的,但是这个时候就会对DMA-BUFFER有所限制,那就这块内存必须是连续的,这对内存紧张的上下文是需要耗费很多CPU资源的。而有了SMMU的帮助,这个内存必须连续的限制就可以被打破。这样会大大提高CPU的效能,给用户带来更加流畅的体验。
关于SMMU Stage-1阶段的使用场景,就介绍到这里。上面的例子中还是围绕SMMU地址翻译的点展开,对于设备隔离、内存保护等场景,大家仔细研究一下上面的细节也会有所收获。比如,CD中存放的EL0空间的虚拟地址的页表基地址等等,感兴趣的研究下去还是很有意思的。

1.2 Stage-2 USE Case(虚拟化架构中SMMU)

Stage-2 场景对应的就是VMSA体系下虚拟化架构下的工作场景,我们对上一个小节中的SMMU的使用场景进行扩展,看一下如果SMMU不支持两级地址翻译会发生什么情况,如图1-4.
1-4

图1-4 High-Level SMMU ARCH

如上图所示,Processor中的驱动和DMA设备中使用相同的输入VA,那么分别经过MMU和SMMU的翻译就会得到相同的PA。此时,无论谁是生产者,谁是消费者,都不会有问题的,因为大家访问的都是正确的地址。如果在虚拟化的架构下,一个GuestOS想使用DMA服务会发生什么情况呢?如图1-5所示。
1-5

图1-5 High-Level SMMU ARCH of GuestOS

此时如果SMMU被禁止使用或者SMMU被设置成Bypass Stage-2阶段的话,那么此时Guest OS中DMA的驱动就无法正确的配置DMA设备。因为无论GuestOS中的驱动怎么努力,它也不可能知道自己咋CPU上获取的实际物理内存的Buffer地址PA,它的能力上限就是看到IPA,自然导致DMA控制器的能力上限也是IPA。而SMMU输出IPA和MMU输出PA肯定是不相等的,此时强行使用DMA肯定会出问题。这种情况下也有解决方案,那就是通过Hypervisor的介入进行必要的翻译工作,通过EL2层面的DMA驱动输入正确的PA给DMA控制器使用。这种方案一方面会增加Hypervisor的编程难度,增加不必要的冗余,最关键的就是如果GuestOS使用DMA特别频繁会极大的增加CPU的负荷,影响CPU上执行的其他任务的体验。
显然上面的情况是我们不希望看见的,解决的方案就是把SMMU配置成支持两级地址翻译,或者至少要支持Stage-2阶段的地址翻译,如图1-6所示。
1-6

图1-6 High-Level SMMU (Stage-2) ARCH of GuestOS

上图中这种方案就可以解决我们遇到的问题,这种方案充分利用了SMMU的硬件能力,分担了CPU的部分算力消耗。而且由于IPA的内存视图在Hypervisor层面是彼此按照VM为单位进行隔离,那么DMA在利用SMMU进行地址翻译的也会自动做到在VM之间隔离,充分保证了虚拟化系统的安全性和稳定性。上面的这个解决方案也是SMMU在soc中以及虚拟化工作场景中的最大意义。

1.3 SMMU的虚拟化架构

按照上一个小结的解决方案配置SMMU,仔细梳理一下大概会有3个方面的优点:
(1) 提高资源利用率:通过共享SMMU硬件资源,可以减少物理硬件的冗余,提高资源利用率。SMMUv3硬件层面就支持两级地址翻译的能力,不用起来肯定是浪费的。
(2) 增强灵活性:SMMU虚拟化允许虚拟机动态地创建、删除和迁移,从而增强了系统的灵活性。其实这也是虚拟化技术的优势,如果VM之间能做到解耦,灵活性那是显而易见的。
(3) 提高安全性:通过SMMU的内存访问控制功能,可以确保虚拟机只能访问授权的内存区域,防止恶意设备或虚拟机之间的资源冲突和安全问题。

这么多好处一定是要付出代价的,俗话说那就是甘蔗没有两头甜的。按照SMMU存在的形态来说,如果是某个IP-Core独占的情况还好一些,如果是多个设备共享使用SMMU的形态存在话,就得对SMMU进行虚拟化处理。本小节,我们会对SMMU的虚拟化架构做一个简要的说明,最重要的是让大家明白SMMU和MMU功能虽然相似但是在EL1的内核中和EL2的Hypervisor中要对它进行大量的虚拟化编程,才能让它在虚拟化的架构下工作起来,说到底SMMU也是SOC总线上的一个设备,而MMU在总线上露脸的是CPU。既然SMMU也是总线上的一个设备,那么它肯定也适用于设备虚拟化的几种方案之一,前面我们专门写了一篇文章来介绍设备的虚拟化技术,建议大家先看一下[V-08][Device Virtualization]-软件虚拟化技术vs硬件虚拟化技术 。按照上面文章中的观点,SMMU肯定是要分享给各个VM使用的,那么选择全虚拟化和Passthrough的方式肯定是不行的。能够选择的也就是半虚拟化方案了,如图1-7所示:
1-7

图1-7 High-Level SMMU Virtualization ARCH

这是一个采用半虚拟化技术的SMMU的虚拟化架构,半虚拟化技术的特点,大家可以参考我们的前序文章,这里我们只介绍SMMUv3虚拟化的框架的相关内容:
(1) 开机以后,Hypervisor中的SMMUv3驱动需要根据配置对SMMUv3硬件进行初始化,这里面除了初始化SMMUv3的相关寄存器之外,最重要的就是根据输入的StreamID初始化STE以及STE下游的数据结构。
(2) 当各个VM被拉起后,各个VM中Kernel中的SMMUv3的驱动也会通过FrontEnd和Backend的链路对SMMUv3的硬件进行CD中相关内容的初始化,当然VM中的相关内容的初始化会受到Hypervisor中驱动的监管。
(3) 在SOC运行的过程中,VM中的SMMUv3驱动也会进行CD的更新以及申请刷新SMMUv3硬件TLBs的操作等等。
(4) 当SMMUv3硬件在工作的过程中发现异常的情况之后,也会通过虚拟化中断的机制像各个VM的vCPU线程抛出中断异常,让VM中SMMUv3接入处理。当然如果异常需要EL2层面的驱动介入处理的话,那么这些异常会被Hypervisor截获并路由给SMMUv3(EL2)的驱动。

由于设备虚拟化的很多预备知识,我们还没有讲解,那么本文中我们只给出了比较粗线条的SMMU的虚拟化架构的介绍。等后面我们补齐相关的知识之后,我们会讲解更加细节的SMMUv3的虚拟化架构。

结语

本文我们介绍了SMMU在Stage-1和Stage-2阶段的使用的一些场景,算是对前序介绍的SMMU背景知识的一个串联,最后我们介绍了SMMUv3的虚拟化架构。对于SMMU的介绍,暂时告一段落,我们已经在这个点上奋战了一个月了,但是实际上我们只介绍了SMMU的一些皮毛,其相关的很多点展开来研究还是非常有意思的,感兴趣的小伙伴可以深入下去。我们必须前进,ARM和虚拟化技术还有跟多的有意思的知识值得我们研究,所以我们还是要继续向前。谢谢大家,请点赞、评论、转发,保持关注。

读到这里的朋友,请歇一歇脚,因为这是很有可能是笔者2024年最后一篇博文了,这里我们稍微做一下总结;
(1) 现有博文的绝大多数都是聚焦于ARM架构和虚拟化的相关领域。由于笔者水平有限,有些主题写得不够透彻,有些点还有谬误之处,感谢大家容人之外的不离不弃,以及一直以来给我鼓励和支持。三人行,必有我师,还是希望大家多多指教,。
(2) 历史博文,笔者只写了一篇文章,原因是历史太过厚重,写了几篇自己一直也不太满意,下一个年度这个主题会有所加强。
(3) 除了上面两个主题之外,笔者还在规划神经网络和数学方面的文章,先立起来一个Flag,请大家耐心等待。

2025的曙光马上就要照亮你我的前程,希望大家新年都健康快乐,平安顺遂, 继续相聚于此,相聚于知识的海洋,探索这个神奇的世界。

Reference

[00] <aarch64_virtualization_100942_0100_en.pdf>
[01] <Armv8-A-virtualization.pdf>
[02] <learn_the_architecture_aarch64_virtualization.pdf>
[03] <DDI0487K_a_a-profile_architecture_reference_manual.pdf>
[04] <DEN0024A_v8_architecture_PG.pdf>
[05] <learn_the_architecture_aarch64_memory_model.pdf>
[06] <80-LX-SMMU-IOMMU-cs0005_linux-DMA-API使用指导.pdf>
[07] <80-V-KVM-k0005_Linux虚拟化KVM-Qemu分析(五)-内存虚拟化.pdf>
[08] <80-LX-SMMU-IOMMU-cs0001_Linux-iommu和vfio概念空间解构.pdf>
[09] <learn_the_architecture_aarch64_memory_management.pdf>
[10] <learn_the_architecture_smmu_software_guide.pdf>
[11] <QNX_SMMUMAN_Users_Guide.pdf>
[12] <IHI0070G.a-System_Memory_Management_Unit_Architecture_Specification.pdf>
[13] <80-ARM-SMMU-wx0001_SMMU学习这一篇就够了(软件硬件原理_模型导读).pdf>
[14] <80-ARM-SMMU-cs0001_ARM-SMMU学习笔记.pdf>

Glossary

MMU - Memory Management Unit
TLB - translation lookaside buffer
VIPT - Virtual Index Physical Tag
VIVT - Virtual Index Virtual Tag
PIPT - Physical Index Physical Tag
VA - Virtual Address
PA - Physical Address
IPS - Intermediate Physical Space
IPA - Intermediate Physical Address
VMID - virtual machine identifier
TLB - translation lookaside buffer
VTTBR_EL2 - Virtualization Translation Table Base Registers
ASID - Address Space Identifier (ASID)
SMMU - System Memory Management Unit
STE - Stream Table Entry
CD - Context Descriptor
GPC - Granule Protection Checks
GPT - Granule Protection Table
ATS - Address Translation Services
ATC - Address Translation Cache
DPT - Device Permission Table
PRI - Page Request Interface
PPRs - PRI Page Requests (PPRs)
VMS - Virtual Machine Structure
CMO - Cache Maintenance Operation
IWB - inner Write-Back
OWB - Outer Write-back
ISH - Inner shareable
PASID - Process Address Space Identifier
ACS - Access Control Services
ITS - Interrupt Translation Service
MSI - Message-Signaled Interrupts
SVA - Shared Virtual Addressing
BA - Bus Address
GBPA - Global ByPass Attribute

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值