👹
Carlos's Tech Blog
  • 🧔ECUs
    • ZYNQ_Documents
      • [ZYNQ] 构建ZYNQ的BSP工程
      • [ZYNQ] 启动流程
      • [ZYNQ] Secure Boot Flow
      • [ZYNQ] Provisioning Guideline
      • [ZYNQ] Decrypting Partition by the Decrypt Agent Using PUF key
      • [ZYNQ] enabling the cryptsetup on ramdisk
      • [ZYNQ] Encrypt external files based on file system using PUF key
      • [ZYNQ] Loading an Encrypted Linux kernel at U-Boot with a KUP Key
      • [ZYNQ] cross-compile the cryptsetup on Xilinx ZYNQ aarch64 platform
      • [ZYNQ] Linux Linaro系统镜像制作SD卡启动
    • S32G_Documents
      • [S32G] Going through the s32g hard/soft platform
      • [S32G] S32g247's Secure Boot using HSE firmware
        • S32g2 HSE key config
        • How S32g verify secure boot image
        • S32g secure boot signature generation
        • How to download and build S32g Secure boot image
        • [S32G] OTA with Secure Boot
    • RT117x_Documents
      • [RT-117x]IMX RT1170 Provisioning Guideline
      • [RT-117x] Going through the MX-RT1170 hard/soft platform
      • [RT-117x] i.MX-RT1170's Secure Boot
        • [RT-117x]Signing image with the HSM (SignServer)
    • LS104x_Documents
      • [LS104x] bsp project
      • [LS104x] boot flow
      • [LS104x] secure boot
      • [LS104x] Application Note, Using the PKCS#11 in TCU platform
      • [LS104x] 使用ostree更新rootfs
      • [LS104x] ostree的移植
      • [LS104x] Starting with Yocto
      • [LS104x] 使用FIT的kernel格式和initramfs
    • IMX6/8_Documents
      • [IMX6] Defining A U-Boot Command
      • NXP IMX6 嵌入式板子一些笔记
      • NXP-imx6 initialization
    • Vehicle_Apps
      • [SecOC] Tree
        • [SecOC] SecOC Freshness and MAC Truncation
  • 😾TECH
    • Rust Arm OS
      • ARMv7m_Using_The_RUST_Cross_Compiler
    • ARM
      • ARM-v7-M
        • 01_ARMv7-M_处理器架构技术综述
        • 02_ARMv7-M_编程模型与模式
        • 03_ARMv7-M_存储系统结构
        • 04_ARMv7-M_异常处理及中断处理
      • ARM-v8-A
        • 02_ARMv8_基本概念
        • 03_ARMv8_指令集介绍_加载指令集和存储指令集
        • 04_ARMv8_指令集_运算指令集
        • 05_ARMv8_指令集_跳转_比较与返回指令
        • 06_ARMv8_指令集_一些重要的指令
        • 0X_ARMv8_指令集_基于汇编的UART驱动
        • 07_ARMv8_汇编器Using as
        • 08_ARMv8_链接器和链接脚本
        • 09_ARMv8_内嵌汇编(内联汇编)Inline assembly
        • 10_ARMv8_异常处理(一) - 入口与返回、栈选择、异常向量表
        • 11_ARMv8_异常处理(二)- Legacy 中断处理
        • 12_ARMv8_异常处理(三)- GICv1/v2中断处理
        • 13_ARMv8_内存管理(一)-内存管理要素
        • 14_ARMv8_内存管理(二)-ARM的MMU设计
        • 15_ARMv8_内存管理(三)-MMU恒等映射及Linux实现
        • 16_ARMv8_高速缓存(一)cache要素
        • 17_ARMv8_高速缓存(二)ARM cache设计
        • 18_ARMv8_高速缓存(三)多核与一致性要素
        • 19_ARMv8_TLB管理(Translation Lookaside buffer)
        • 20_ARMv8_barrier(一)流水线和一致性模型
        • 21_ARMv8_barrier(二)内存屏障案例
      • ARM Boot Flow
        • 01_Embedded_ARMv7/v8 non-secure Boot Flow
        • 02_Embedded_ARMv8 ATF Secure Boot Flow (BL1/BL2/BL31)
        • 03_Embedded_ARMv8 BL33 Uboot Booting Flow
      • ARM Compiler
        • Compiler optimization and the volatile keyword
      • ARM Development
        • 在MACBOOK上搭建ARMv8架构的ARM开发环境
        • Starting with JLink debugger or QEMU
    • Linux
      • Kernel
        • 0x01_LinuxKernel_内核的启动(一)之启动前准备
        • 0x02_LinuxKernel_内核的启动(二)SMP多核处理器启动过程分析
        • 0x21_LinuxKernel_内核活动(一)之系统调用
        • 0x22_LinuxKernel_内核活动(二)中断体系结构(中断上文)
        • 0x23_LinuxKernel_内核活动(三)中断体系结构(中断下文)
        • 0x24_LinuxKernel_进程(一)进程的管理(生命周期、进程表示)
        • 0x25_LinuxKernel_进程(二)进程的调度器的实现
        • 0x26_LinuxKernel_设备驱动(一)综述与文件系统关联
        • 0x27_LinuxKernel_设备驱动(二)字符设备操作
        • 0x28_LinuxKernel_设备驱动(三)块设备操作
        • 0x29_LinuxKernel_设备驱动(四)资源与总线系统
        • 0x30_LinuxKernel_设备驱动(五)模块
        • 0x31_LinuxKernel_内存管理(一)物理页面、伙伴系统和slab分配器
        • 0x32_LinuxKernel_内存管理(二)虚拟内存管理、缺页与调试工具
        • 0x33_LinuxKernel_同步管理_原子操作_内存屏障_锁机制等
        • 01_LinuxDebug_调试理论和基础综述
      • Userspace
        • Linux-用户空间-多线程与同步
        • Linux进程之间的通信-管道(上)
        • Linux进程之间的通信-管道(下)
        • Linux进程之间的通信-信号量(System V)
        • Linux进程之间的通信-内存共享(System V)
        • Linux进程之间的通信-消息队列(System V)
        • Linux应用调试(一)方法、技巧和工具 - 综述
        • Linux应用调试(二)工具之coredump
        • Linux应用调试(三)工具之Valgrind
        • Linux机制之内存池
        • Linux机制之对象管理和引用计数(kobject/ktype/kset)
        • Linux机制copy_{to, from}_user
        • Linux设备树 - DTS语法、节点、设备树解析等
        • Linux System : Managing Linux Services - inittab & init.d
        • Linux System : Managing Linux Services - initramfs
      • Kernel Examples
        • Linux Driver - GPIO键盘驱动开发记录_OMAPL138
        • 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(一)之miscdevice和ioctl
        • 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(二)之cdev与read、write
        • 基于OMAPL138的字符驱动_GPIO驱动AD9833(三)之中断申请IRQ
        • Linux内核调用SPI驱动_实现OLED显示功能
        • Linux内核调用I2C驱动_驱动嵌套驱动方法MPU6050
    • OPTEE
      • 01_OPTEE-OS_基础之(一)功能综述、简要介绍
      • 02_OPTEE-OS_基础之(二)TrustZone和ATF功能综述、简要介绍
      • 03_OPTEE-OS_系统集成之(一)编译、实例、在QEMU上执行
      • 05_OPTEE-OS_系统集成之(三)ATF启动过程
      • 06_OPTEE-OS_系统集成之(四)OPTEE镜像启动过程
      • 07_OPTEE-OS_系统集成之(五)REE侧上层软件
      • 08_OPTEE-OS_系统集成之(六)TEE的驱动
      • 09_OPTEE-OS_内核之(一)ARM核安全态和非安全态的切换
      • 10_OPTEE-OS_内核之(二)对安全监控模式的调用的处理
      • 11_OPTEE-OS_内核之(三)中断与异常的处理
      • 12_OPTEE-OS_内核之(四)对TA请求的处理
      • 13_OPTEE-OS_内核之(五)内存和cache管理
      • 14_OPTEE-OS_内核之(六)线程管理与并发
      • 15_OPTEE-OS_内核之(七)系统调用及IPC机制
      • 16_OPTEE-OS_应用之(一)TA镜像的签名和加载
      • 17_OPTEE-OS_应用之(二)密码学算法和安全存储
      • 18_OPTEE-OS_应用之(三)可信应用的开发
      • 19_OPTEE-OS_应用之(四)安全驱动开发
      • 20_OPTEE-OS_应用之(五)终端密钥在线下发系统
    • Binary
      • 01_ELF文件_目标文件格式
      • 02_ELF文件结构_浅析内部文件结构
      • 03_ELF文件_静态链接
      • 04_ELF文件_加载进程虚拟地址空间
      • 05_ELF文件_动态链接
      • 06_Linux的动态共享库
      • 07_ELF文件_堆和栈调用惯例以ARMv8为例
      • 08_ELF文件_运行库(入口、库、多线程)
      • 09_ELF文件_基于ARMv7的Linux系统调用原理
      • 10_ELF文件_ARM的镜像文件(.bin/.hex/.s19)
    • Build
      • 01_Script_makefile_summary
    • Rust
      • 02_SYS_RUST_文件IO
    • Security
      • Crypto
        • 1.0_Security_计算机安全概述及安全需求
        • 2.0_Security_随机数(伪随机数)
        • 3.0_Security_对称密钥算法加解密
        • 3.1_Security_对称密钥算法之AES
        • 3.2_Security_对称密钥算法之MAC(CMAC/HMAC)
        • 3.3_Security_对称密钥算法之AEAD
        • 8.0_Security_pkcs7(CMS)_embedded
        • 9.0_Security_pkcs11(HSM)_embedded
      • Tools
        • Openssl EVP to implement RSA and SM2 en/dec sign/verify
        • 基于Mac Silicon M1 的OpenSSL 编译
        • How to compile mbedtls library on Linux/Mac/Windows
    • Embedded
      • eMMC启动介质
  • 😃Design
    • Secure Boot
      • JY Secure Boot Desgin
    • FOTA
      • [FOTA] Module of ECUs' FOTA unit design
        • [FOTA] Tech key point: OSTree Deployment
        • [FOTA] Tech key point: repositories role for onboard
        • [FOTA] Tech key point: metadata management
        • [FOTA] Tech key point: ECU verifying and Decrpting
        • [FOTA] Tech key point: time server
      • [FOTA] Local-OTA for Embedded Linux System
    • Provisioning
      • [X-Shield] Module of the Embedded Boards initialization
    • Report
由 GitBook 提供支持
在本页
  • 19_ARMv8_TLB管理(Translation Lookaside buffer)
  • 0. 引言
  • 1. TLB Basic
  • 1.1 TLB性能影响因素
  • 1.2 TLB在SoC位置
  • 1.3 ASID
  • 1.4 TLB指令
  • 1.5 TLB案例
  • 2. REF
  1. TECH
  2. ARM
  3. ARM-v8-A

19_ARMv8_TLB管理(Translation Lookaside buffer)

https://github.com/carloscn/blog/issues/60

上一页18_ARMv8_高速缓存(三)多核与一致性要素下一页20_ARMv8_barrier(一)流水线和一致性模型

最后更新于1年前

19_ARMv8_TLB管理(Translation Lookaside buffer)

0. 引言

TLB这块我们之前在学习MMU的时候有过涉及。TLB作为一个MMU的旁路器件辅助CPU以最快的速度能找出虚拟地址和物理地址之间的对应关系,之前的文章只是泛泛的介绍了TLB的功能,并没有一个比较感性的认识。这一节内容我们来重新认识一下TLB, 并且将TLB和cache的结构做对比,最后我们引入一个2017年在CPU界发生的重大漏洞“CPU熔断”,这个漏洞在ARM上就是在TLB上面做了一些工作来解决这个安全漏洞的。


1. TLB Basic

TLB是一个很小的高速缓存,是MMU的一个辅助机。TLB包含的项(TLB Entry)数量比较少,每个TLB项包含页帧号(VPN),物理也帧号(PFN)以及一些属性。当处理器要访问一个虚拟地址的时候,首先会从TLB中查找是不是包含这个地址,如果命中皆大欢喜;如果不命中TLB将会访问MMU,从MMU中获取目标虚拟地址对应的物理地址(规则按照多级页表查询的方法),并组成TLB项,替换到自己的TLB列表内。

1.1 TLB性能影响因素

在ARMv8中,最高等级的页表支持4级,如果没有TLB,那么每次内存访问将要进行4级页表的索引,这样大大的降低了效率。我们不妨引入一个有趣的实验,在Cortex-A9硬件下,测试TLB的影响。这个例子来源于StackOverflow,使用三星Galaxy S3做了测试,相关配置如下:

  • I-cache和D-cache各配套一个TLB,我们非正式定义为I-TLB(32-entries or 64-entries),D-TLB(32-entries)

  • 在L2 cache上还有个main TLB,我们命名为M-TLB

这个论文里面在比较老的机器上CRAY-T3D和DEC workstation上面对cache进行探究,即便是比较老了,但是还具备参考意义,可以好好读一读。

image-20220518100008470

在三星的这个芯片中,有32个TLB entry,随着enties的数量升高,所要查询时间越高,甚至在在260个entry之后,性能出现了极具的衰减,因此可以得到结论,并不是TLB越大性能越好。

1.2 TLB在SoC位置

ARMv8-A的体系结构手册没有约定TLB项的结构,我们在cortex-A72的手册中来找到一些参考的设计,在其他的SoC中,TLB的位置并不是一样的。下图是Cortex-A72 cluster 4个cores中的TLB的位置(25页),所有的TLB都是和cache紧挨着的。每个core上面针对D-cache存在一个d-tlb,对于i-cache有一个配套的i-tlb,还有与L2-cache配套的L2-TLB。

根据介绍,Cortex-A72的每一个核的TLB大小为:

  • L1-I-TLB:48-entry fully-associative (4KB/64KB/1MB page size),PIPT结构

  • L1-D-TLB: 32-entry fully-associative (4KB/64KB/1MB page size),PIPT结构

  • L2-TLB: 4-way set-associative unified 1024-entry。

这里引入这个就是让我们有一个定量的概念,一级的TLB的项目并不是很多,48/32这种;而二级TLB能达到1024个entry。在L1 TLB采用的是全相联的方式,可以视为一个1维的,没有什么可说的;

而L2的cache采用了组相联的方式,如图所示:

1.3 ASID

芯片工程师和操作系统设计人员共同努力需要解决的问题。在芯片层级上,引入了上面结构提到的ASID (Address space ID)。

1.3.1 进程切换的效率问题

单核场景

首先感谢提供两种思路。我们先看一下单核时候在多进程进行切换的时候TLB的变化,从进程的角度来看,这里有三类空间:

  • 进程1的空间(我们定义,用户空间为S_P1U,内核空间为S_P1K)

  • 进程2的空间(我们定义,用户空间为S_P2U,内核空间为S_P2K)

  • kernel的空间 (S_K)

CPU上面运行了这些P1和P2还有K的进程,对于K进程而言还算是没有什么问题所有的进程都是共享这部分,而对于P1和P2都有自己的用户地址空间。比如,现在这样的状况,从进程的视角来看,P1和P2有着一样的虚拟地址空间,但是在进程的TLB上,却有着不一样的映射。在CPU上面正常的P1和P2的映射关系如图,他们分别映射不一样的位置。假如P1切到P2没有刷新TLB的时候,CPU就会在P2的时候使用了P1的地址,这就出现了TLB的同名问题(TLB其歧义问题)

TLB的歧义问题

我们知道不同的进程之间看到的虚拟地址范围是一样的,所以多个进程下,不同进程的相同的虚拟地址可以映射不同的物理地址。这就会造成歧义问题。例如,进程A将地址0x2000映射物理地址0x4000。进程B将地址0x2000映射物理地址0x5000。当进程A执行的时候将0x2000对应0x4000的映射关系缓存到TLB中。当切换B进程的时候,B进程访问0x2000的数据,会由于命中TLB从物理地址0x4000取数据。这就造成了歧义。如何消除这种歧义,我们可以借鉴VIVT数据cache的处理方式,在进程切换时将整个TLB无效。切换后的进程都不会命中TLB,但是会导致性能损失。

如果我们粗暴的flush整个TLB这个会带来性能上极大的损失,所以有没有一种方法可以降低TLB的刷新几率呢?当然有,对于内核空间而言,这部分空间无论是哪个进程,他们都是一致的,所以这部分就算是切换掉,也不需要flush;而对于用户空间而言,对于他们来说是私有的,因此就需要flush操作。因此,在ARM上将TLB区分为全局性的TLB和局部性TLB。

为了支持标识TLB的类别,则使用ASID(Address Space ID)来进行编址。有了ASID的硬件方案,进程切换的时候根据ASID来判断自己是否需要flush TLB。在ARMv8-A还有ARMv9-A中使用寄存器TTBRx_TL1来维护ASID的值。

ARMv8有两个TTBR,一个是0,一个是1,我们在使用的时候使用任意一个都可以,只不过需要在TCR寄存器的A1的域来选择那个TTBR生效。ASID并不是进程的PID,ASID使用的bitmap模式管理,具体如何管理参考。如果是8位的bitmap,那么就有256个号码同时存在可以区分。实际上L2的页表应该如下所示:

多核场景

进程调度多进程,可能存在的情况比单核的时候要复杂,因为TLB在每个core上面都有,而且TLBs是相互隔离的不共享任何数据的。大体可分为两种情况:

  • 多个进程被调度一个CPU管理,那么TLB的问题如单核场景是一致的。

  • 多个进程被调度多个CPU管理,那么在切换多核之前,系统要对TLB进行flush,为新切入的进程准备一个干净的TLB。如下图所示 。

在多核系统有个问题需要注意:

不过,对于多核系统,这种情况有一点点的麻烦,其实也就是传说中的TLB shootdown带来的性能问题。在多核系统中,如果cpu支持PCID并且在进程切换的时候不flush tlb,那么系统中各个cpu中的tlb entry则保留各种task的tlb entry,当在某个cpu上,一个进程被销毁,或者修改了自己的页表(也就是修改了VA PA映射关系)的时候,我们必须将该task的相关tlb entry从系统中清除出去。这时候,你不仅仅需要flush本cpu上对应的TLB entry,还需要shootdown其他cpu上的和该task相关的tlb残余。而这个动作一般是通过IPI实现(例如X86),从而引入了开销。此外PCID的分配和管理也会带来额外的开销,因此,OS是否支持PCID(或者ASID)是由各个arch代码自己决定(对于linux而言,x86不支持,而ARM平台是支持的)。

1.4 TLB指令

  • 使所有的TLB表失效

  • 使ASID对应的某一个TLB项失效

  • 使ASID对应的所有的TLB失效

  • 使虚拟地址对应的所有TLB失效

TLB <type><level>{IS} {<xt>}

1.5 TLB案例

这部分在Linux内核机制部分,不是本文的重点,我们在这里做一些简单的论述。2018年的时候,各大媒体开始渲染“幽灵”和“熔断”的CPU漏洞,说Intel有重大的bug,后来ARM和AMD也相继确认的确有这样的漏洞影响。Meltdown(熔断)对应编号恶意数据缓存加载 CVE-2017-5754 ,Spectre (幽灵)对应编号边界检查绕过 CVE-2017-5753 、分支目标注入 CVE-2017- 。从测试现象来看,就是在用户空间的程序可以无权限的访问kernel space的内存,因此就导致很多在kernel space上的机密数据泄露出来。

这次漏洞利用了 CPU 执行中对出现故障的处理。由于现在 CPU 为了提供性能,引入了乱序执行和预测执行。

  • 乱序执行是指 CPU 并不是严格按照指令的顺序串行执行,而是根据相关性对指令进行分组并行执行,最后汇总处理各组指令执行的结果。

  • 预测执行是 CPU 根据当前掌握的信息预测某个条件判断的结果,然后选择对应的分支提前执行。

这两种执行在遇到异常时,CPU 会丢弃之前执行的结果,将 CPU 的状态恢复到乱序执行或预测执行前的正确状态,然后继续执行正确的指令,从而保证了程序能够正确连续的执行。但是问题在于,CPU 恢复状态时并不会清除 CPU 缓存中的内容,而这两组漏洞正是利用了这一设计上的缺陷进行侧信道攻击。乱序执行对应的利用即为 Meltdown ,而预测执行对应的利用即为 Spectre 。

CPU架构级的漏洞很难修补,只能通过软件来进行一些弥补,因此Linux kernel方案就是采用KPTI的方案:

  • KPTI方案

  • BBM机制

2. REF

  1. Empirical_Evaluation_of_the_cray-t3d_a_compiler_perspecitve.pdf

  2. TLB原理

  3. Arm Armv9-A Architecture Registers - TTBR1_EL1, Translation Table Base Register 1 (EL1)

  4. Arm A-profile A64 Instruction Set Architecture - TLBI

性能图
😾