目錄
0、回顧
1、TLB
1.1、TLB coherency
2、MMU
2.1、TTBR0、TTBR1、TTBCR
2.1.1、TTBCR
2.1.2、TTBR0、TTBR1
2.2、Translation Table
2.2.1、L1 Address Translation
2.2.2、L2 Address Translation
3、OS Usage Of Translation Tables
3.1、Address Space ID(ASID)
3.2、TTBR0、TTBR1
之前寫過 MMU 的一些入門和基礎的分析《初探 MMU》和《ARMv7-A 的 MMU 淺析》,有基于概念掌握和基本入門的一些了解,這裡打算在針對 ARMv7-A 的處理器再次稍微深入一點研究一下他的 MMU 和 TLB;
這一版同樣基于 ARM 官方文檔:
ARMv7-A_and_R_Architecture_Reference_Manual
DEN0013D_cortex_a_series_PG
的一個是完整版的 ARMv7-A 的處理器架構文檔,第二個是 Cortex-A 系列的 Programmer Guide;
由于有前面的兩篇文章墊底(《初探 MMU》和《ARMv7-A 的 MMU 淺析》),這裡就不再介紹一些基礎的内容了,直接單刀直入;
0、回顧
前面知道,MMU 用作虛拟位址和實體位址的互相轉換,是為了能夠給 OS 提供統一視角的虛拟位址空間;
TLB 的作用是作為 MMU 的 Cache,以提高 MMU 的性能,他們之間的關系如下:
1、ARM 處理器發出位址通路(虛拟位址),首先過 MMU 位址翻譯單元的 TLB,如果 TLB 命中,那麼直接傳回真實的實體位址;
2、如果 TLB Miss,那麼就要靠 Table Walk 單元去主存中查找表,以擷取實體位址,然後通過 Cache,去通路;
3、Cache 如果命中,那麼直接傳回實際實體位址的資料,否則,也就是最糟糕的情況,會去通路主存的資料;
上面的過程呢,軟體要做的,隻有配置并放好這個 Transliation Tables,其他的過程,全部是硬體行為;下面馬上仔細的過這部分的細節;
使能 MMU 的參考代碼(因為是 CP15 的系統控制寄存器,是以使用 MRC/MCR 指令):
MRC p15, 0, R1, c1, C0, 0 ;Read control register
ORR R1, #0x1 ;Set M bit
MCR p15, 0,R1,C1, C0,0 ;Write control register and enable MMU
這裡要注意的一點是,可能要用到記憶體屏障指令,因為這裡就開啟了 MMU,即将進入虛拟記憶體的世界,要確定在這之前,流水線幹淨,是以執行已經完畢;
1、TLB
TLB 的全稱是:Translation Lookaside Buffer;從第一節的那個圖可以看出來,MMU 做 Table Walk 的這個 Transliation Tables 是放到主存中,主存通路速度很慢(加 Cache 的根本原因),是以,這裡每次都去再主存中做 Table Walk,顯然效率非常低,是以,這裡就為這個 Table Walk 定制了一個屬于他的 “Cache”,稱之為 TLB;
但是與 真是的 Cache 不一樣(詳見《ARMv7-A 處理器窺探(4) —— Cache》),這個 TLB 是專門緩存 Transliation Tables 的,典型的情況,他的組成如下:
由 VA、ASID、PA、Attributes 組成,即:
VA:虛拟位址;
PA:實體位址;
ASID:Address Space ID;
Attributes:屬性;
1.1、TLB coherency
TLB 既然扮演的 Transliation Tables Cache 的角色,那麼也會有一緻性問題,最典型的就是再 OS 中,上下文切換的時候,上一個程序的虛拟位址對應的實體位址表,肯定是和另一個不一樣,導緻 TLB 一緻性問題;此刻,OS 必須處理這種情況,使得上一個程序的 TLB 對下一個失效,也可以直接通過 CP15 控制寄存器,來 flush 掉 TLB(代價太大);
2、MMU
這裡,抛開大實體位址擴充和 section 和 supersection 的分析,暫時就看最最常用的兩段查找;兩段頁表查找,我們稱第一級頁表為 L1,第二級為 L2;
2.1、TTBR0、TTBR1、TTBCR
前面知道,軟體需要負責建構這個虛拟位址到實體位址的轉換表:Transliation Tables,當軟體構件完畢這個表後,隻需要告訴硬體,這個 Transliation Tables 放到了那個首位址即可,這個配置通過寫 ARM 的 TTBR 寄存器實作(Translation Table Base Address );這裡其實有兩個 TTRB 寄存器,分别叫 TTBR0 和 TTBR1,為啥兩個,後面解釋;
2.1.1、TTBCR
和這個 TTBR0、TTBR1 勾肩搭背的,還有一個 TTBCR 寄存器,他們直接什麼關系呢,看寄存器說明:
TTBCR:
PD0 和 PD1 是和 Security Extensions 相關的,不管他;
EAE 是和 Large Physical Address Extension 相關的,不管他;
主要關注這裡的 N[2:0],訓示TTBR0頁表基址寄存器基址位寬,同時訓示使用 TTBR0 還是 TTBR1 作為頁表基址寄存器,以及 TTBR0 頁表尺寸:
如果 N = 0 的話,則在做 Table Walk 的時候使用 TTBR0 指定的基位址作為 Transliation Tables 入口的位址;
如果 N > 0 的話:訓示TTBR0頁表基址寄存器基址位寬,同時訓示使用 TTBR0 還是 TTBR1 作為頁表基址寄存器,以及 TTBR0 頁表尺寸;
- N==0,使用 TTBR0。
- N>0,如果虛拟位址[31:32-N]為0,則使用 TTBR0;其他情況使用TTRB1。這種情況下,N 訓示了TTBR1的頁表位址,也訓示了 TTBR0 的頁表大小。
- TTRB0的頁表大小由TTBCR.N控制,TTRB1的頁表大小為16KB。
我換句話來說,當 N>0 的時候,比如 N=1,那麼按照這種說法,VA [31:31] 也就是 VA 的 bit[31] 為 0 的時候,使用 TTBR0 否則使用 TTRB1,按照位址空間來劃分,即,32bits 位址,當最高位為 0,即虛拟位址為 0x0000_0000 ~ 0x7FFF_FFFF 這個區間的時候,使用 TTBR0 作為 Transliation Tables 入口的位址,從 0x8000_000 ~ 0xFFFF_FFFF 的虛拟位址空間,使用 TTBR1;
ARM 官方舉了個例子,當 TTBCR.N=3‘b111 的時候,VA [31:25] 全部為 0 的時候,使用 TTBR0,按照位址空間來劃分就是,虛拟位址為:0x0000_0000 ~ 0x01FF_FFFF 這段區間使用 TTBR0 作為 Transliation Tables 入口的位址;
0x0200_0000 ~ 0xFFFF_FFFF 的虛拟位址空間,使用 TTBR1;
OK,現在可以了解為,配置 TTBCR.N 這個值,可以實作将虛拟位址切割成為兩部分,一部分使用 TTBR0 指定的 Transliation Tables 進行 Table Walk,另一部分使用 TTBR1 指定的 Transliation Tables 進行 Table Walk,這個有什麼好處呢?比如,核心的頁表,是不會改變的,而程序上下文的頁表是會改變的,有了這個的話,就可以考慮用一個 TTBR 來專門為核心服務,另一個 TTBR 為程序服務,這樣避免程序和核心使用同一個頁表,每次都要進行核心頁表的拷貝;
由于 TTBCR 是 CP15 的寄存器,通路 TTBCR 的指令為:
MRC p15, 0, <Rt>, c2, c0, 2 ; Read TTBCR into Rt
MCR p15, 0, <Rt>, c2, c0, 2 ; Write RT to TTBCR
2.1.2、TTBR0、TTBR1
上面說了 TTBCR,下面來看 TTRB0、TTRB1 寄存器描述:
TTBR0
在帶有多核處理器擴充的情況下 TTBR0 由一個可變的長度構成 Transliation Tables Base Address,這個 x 就是上面的 (14 - (TTBCR.N));
Bits[31:x]:x=(14-(TTBCR.N))。一級頁表位址;
Bits[x-1:7]:Reserved;
NOS:Not Outer Shareable bit,訓示了做 Table walk 的那個記憶體的屬性,是 Outer Shareable 還是 Inner Shareable.
- 0 Outer Shareable.
- 1 Inner Shareable.
TTBR0.S == 0 時,該bit無效;
S:Shareable bit. 訓示記憶體共享屬性與頁表轉換的關系;
- 0 Non-shareable.
- 1 Shareable.
RNG:Region bits,訓示 Outer Cache 屬性與頁表轉換的關系;
- 0b00 Normal memory, Outer Non-cacheable.
- 0b01 Normal memory, Outer Write-Back Write-Allocate Cacheable.
- 0b10 Normal memory, Outer Write-Through Cacheable.
- 0b11 Normal memory, Outer Write-Back no Write-Allocate Cacheable.
IRGN[6,0]:Inner region bits,訓示 Inner Cache 屬性與頁表轉換的關系;
- 0b00 Normal memory, Inner Non-cacheable.
- 0b01 Normal memory, Inner Write-Back Write-Allocate Cacheable.
- 0b10 Normal memory, Inner Write-Through Cacheable.
- 0b11 Normal memory, Inner Write-Back no Write-Allocate Cacheable.
通路 TTBR0 的指令為:
MRC p15, 0, <Rt>, c2, c0, 0 ; Read 32-bit TTBR0 into Rt
MCR p15, 0, <Rt>, c2, c0, 0 ; Write Rt to 32-bit TTBR0
TTBR1
它的位域和 TTBR0 幾乎一樣,唯一不一樣的地方在于,配置的位址區間在于 bit[31:14],這意味着,配置進 TTBR1 的 Transliation Tables Base Address 的實體位址,必須 16KB 對齊;
通路 TTBR1 的指令為:
MRC p15, 0, <Rt>, c2, c0, 1 ; Read 32-bit TTBR1 into Rt
MCR p15, 0, <Rt>, c2, c0, 1 ; Write Rt to 32-bit TTBR1
2.2、Translation Table
現在我們知道了合理的配置 TTBCR/TTBR0/TTBR1 可以配置設定并指定 Transliation Tables,而這個 Transliation Tables 位于記憶體中,用作 MMU 來做 Table Walk;那麼接下來我們需要知道頁表的結構,這樣我們才能夠在記憶體中建立頁表,并将頁表配置給 TTBR 寄存器,完成 MMU 的配置;
不考慮大位址擴充和 SuperSection 以及 Section 的情況下,針對 Transliation Tables,ARMv7-A 的手冊給出了如下的圖解
圖中,我們暫時隻考慮 Page Table 的情況,即紅色部分(其餘的可以照着推);
藍色的部分,可以了解為之前寄存器裡面配置的那個 N 值;這裡為了說明情況,我們暫時将 N 定為 0;
2.2.1、L1 Address Translation
我們先暫時不管使用 TTBR0 還是 TTBR1,其實過程是一樣的;此刻當 N = 0 的時候,一級頁表以虛拟位址(後面簡稱 VA,即 Virtual Address)VA[31:20] 作為 L1 Index,一共 12bits,最大能夠表征 4K 的 Index:
每個入口是 4 Bytes 也就是一個 Word,32bits 的入口,L1 Index 從 0~4095,一共 4K,在記憶體上,每個入口 4Bytes,那麼 L1 頁表占用記憶體 4K x 4 Bytes = 16KB;
每一個入口是什麼樣子的呢,我們放大來看:
可以看到,這個入口,根據不同的配置,内容有所差別,一共有 4 種類型,這 4 種類型,通過 32bits 的尾部 2 bits 來區分,即,綠色部分(Section 和 SuperSection 的區分,還靠 bit[18]);
這裡我們暫時不關心 Section 和 SuperSection,關注于紅色部分和那個 Fault;
Level 2 Descriptor Base Address:指向的是 L2 頁表的實體位址的基位址;可以看到他是 bit[31:10],是 1KB 邊界對齊的;
這個 Domain 指的是 ARM 支援将記憶體标記為最多 16 個 domain,并以 Domain ID 作為區分,每個 Domain 可以支援配置成為不同的通路權限(通過配置 CP15 的 C3 的 Domain Access Control Register (DACR) 寄存器):
配置指令為:
MRC p15, 0, <Rt>, c3, c0, 0 ; Read DACR into Rt
MCR p15, 0, <Rt>, c3, c0, 0 ; Write Rt to DACR
針對這個 DACR 寄存器,ARM 官方建議配置成為 Client;
The use of domains is deprecated in the ARMv7 architecture, and will eventually be removed,
but in order for access permissions to be enforced, it is still necessary to assign a domain number
to a section and to ensure that the permission bits for that domain are set to client. Typically, you
would set all domain ID fields to 0 and set all fields in the DACR to ‘Client’.
2.2.2、L2 Address Translation
介紹完 L1 Address Translation 後,下面是二級頁表!與 L1 頁表不一樣,二級頁表不和 N 值挂鈎,它直接采用 VA[19:12] 作為 L2 Index 索引,一共 8 bits,最大能夠表征 256 的 L2 Index;
加入 L2 頁表後結合 L1,通過一個給定的 VA 進行索引的第二步為(圖中表示的 N 值為 0):
這樣,一個 VA 通過高位址部分[31:20] 索引到了 L1,再從 L1 指向的 L2 加上 VA[19:12] 作為 L2 Index,索引到 L2 表的固定位置;
L2 也是每條由 4 Bytes 構成,即一個 32bits 的數,那麼一個 L2 表大小為 256 x 4 Bytes = 1024 Bytes = 1KB;一共有 4096 個這樣的 L2,那麼 L2 表總的大小為:4096 x 1KB = 4MB;
我們放大每一條 L2 的入口:
我們隻關心紅色部分!可以看到,這個 Small Page Base Address 有 bit[31:12] 也就是 4KB 邊界對齊!接下來我們看剩餘幾個位的含義:
AP/APX:Access Permission 即通路權限,每個記憶體區域 都有自己的權限,不符合通路權限的 記憶體通路都會引發 異常。如果是 資料通路 則引發 資料異常。如果是 指令通路,且該指令在執行前沒有被 flush,将引發 預取指異常。引發的 異常原因将會被設定在 CP15 的 the fault address and fault status registers;
記憶體區域類型 可以通過 TEX字段、C字段 和 B字段 來進行設定:
XN:指的是 Execute Never,不允許執行,如果往這裡取位址執行,那麼會導緻異常發生;通常,Device memory 類型的區域會配置成為 XN;
S:指的是是否具有 Shareable 屬性;
nG:non-Global,這個标記告訴 MMU,這個頁表是否是一個全局的,什麼意思呢?看下面:
當 nG 為 0 的時候,說明此區域是全局可見的,換句話來說,就是任何時候都生效;
當 nG 為 1 的時候,說明此區域不是全局的,要聯合這個 ASID 來确認;
每一個 nG=1 的區域,都會和 ASID 來關聯,ASID (Address Space Identifier),這代表,TLB 可以存在多個不同程序的頁表緩存,後面說 ASID 的時候會仔細說;
自此,L1/L2 分析完畢,那麼整個 Table Walk 的流程為:
VA 的 4K 頁内偏移,直接對應到 PA 的 4KB 頁内偏移;
3、OS Usage Of Translation Tables
通常情況下,在使用 Cortex-A 系列處理器的時候,典型場景是跑多任務 OS;每一個任務(或者成為應用),都有它獨立的虛拟位址空間,以及他的獨立的 Translation Table;但是對于 OS 來說,Kernel 的 Translation Tables 其實是固定的,隻是程序之間的 Translation Tables 不一樣而已;
當一個程序啟動的時候,OS 負責為他 code 和 data 段建立映射表(Translation Tables);當程序調用諸如 malloc 之類配置設定記憶體的行為,OS 負責修改 Translation Tables(Linux 中,實際通路配置設定的記憶體的時候,才去修改頁表),程序生命周期消亡,OS 負責回收它的資源和頁表,并可以為下一個新的程序提供資源;每一個程序都有自己的獨立的頁表,這便可以保證程序之間不會互相幹擾;
3.1、Address Space ID(ASID)
在作業系統中,多程序是一種常态。那麼多程序 的情況下,每次切換程序都需要進行 TLB 清理。這樣會導緻切換的效率變低。為了解決問題,TLB 引入了 ASID(Address Space ID) 。ASID 的範圍是 0-255。ASID 由作業系統配置設定,目前程序的ASID值 被寫在 ASID 寄存器 (使用CP15 c3通路)。TLB 在更新頁表項時也會将 ASID 寫入 TLB。
如果設定了如果 目前程序的ASID,那麼 MMU 在查找 TLB 時, 隻會查找 TLB 中具有 相同ASID值 的 TLB行。且在切換程序是,TLB 中被設定了 ASID 的 TLB行 不會被清理掉,當下次切換回來的時候還在。是以ASID 的出現使得切換程序時不需要清理 TLB 中的所有資料,可以大大減少切換開銷。
有了這個 ASID + nG 的機制,那麼 TLB 中就可以緩存不同程序的頁表,不用每次都去 flush TLB,導緻性能的損失:
3.2、TTBR0、TTBR1
前面我們說了 TTBR0、TTBR1 是根據 TTBCR.N 來進行劃分的,典型場景下 OS 跑多任務,如果處理器隻能夠支援一個 TTBR 的話,也就意味着使用者空間和核心空間使用同一個 TTBR,由于核心空間的 code 和 data 幾乎是不變的,但是多任務的使用者空間都是不一樣的,這樣就會存在兩個問題:
1、多個任務的頁表裡面,都有同樣一部分核心映射的拷貝副本;
2、要修改核心映射的時候,所有任務的頁表都要修改;
加入兩個 TTBR 的原因,是因為希望核心和使用者空間使用兩套 TTBR,這樣就可以避免上面的尴尬;核心空間固定使用一組,使用者空間不斷的切換(比如,配合 TTBR0 + ASID 進行性能的提升)
參考文獻:
https://www.jianshu.com/p/ef1e93e9d65b
https://www.cs.rutgers.edu/~pxk/416/notes/10-paging.html
https://blog.csdn.net/liyuewuwunaile/article/details/106773630?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242