AMRv8內存管理簡介

date: 2017-8-28linux

1 ARMv8架構簡介

1.1背景

2011年,ARM推出了第8代架構ARMv8(ARMv4以前的架構已經被廢棄),ARMv8架構是迄今爲止ARM歷史上變革最大的架構。編程

若是知道了架構的歷史背景,以及設計者的設計理念,那麼理解架構的行爲方式便很容易了。爲了方便後續的研究,咱們先來回顧下ARMv8的歷史背景(具體可參考ARMv8白皮書,連接地址爲:http://www.arm.com/zh/files/downloads/ARMv8_white_paper_v5.pdf,感興趣的同窗能夠自行閱讀)。緩存

ARM爲低功耗而生,過去20年,基於RISC指令集的ARM芯片在各個領域得到普遍的成功。除了移動設備領域,ARM芯片也已經悄悄滲透到PC領域(基於ARM的平板能夠給用戶帶來PC級的體驗,尤爲是win10宣佈支持ARM)、企業與服務器領域(這些領域一直是intel的天下)。爲了更精準的響應市場的需求,從ARMv7開始,ARM將其架構分爲三個品類,分別是:安全

  1. A類,針對Application,適用於追求高性能的場景,好比移動領域(手機)或者企業領域(服務器)
  2. R類,針對Real-time,適用於車用以及工業控制領域
  3. M類,針對Microcontroller,適用於微控制器領取

2011年10月,ARM推出了Cortex-A7以及大小核架構,再一次振奮了業界,在延遲電池壽命的同時,爲用戶提供優秀智能手機的體驗。Cortex系列迅速成爲ARM新業務中的王牌。服務器

ARM芯片的性能上去了,那些包含複雜運算的大致量程序能夠開心的跑起來了。但問題來了,這些大致量的程序須要更多的地址空間(好比超過4G的地址空間),因而AMR在AMRv7中增長了LAPE(Large Physical Address Extensions),使用48位虛擬地址,並將虛擬內存映射到40位物理內存地址上(支持高達1024G的物理內存)。網絡

利用LAPE,內存空間的問題算是暫時解決了,但這隻能是補救方案。若是你想繼續搞企業級產品,想接着玩服務器,不搞64位操做系統行嗎?不搞虛擬化行嗎?所以ARM在設計v8架構時,它面臨下面幾個「剛需」:架構

  1. 支持64位
  2. 虛擬化支持
  3. 安全加強

1.2運行狀態

因爲基於32位ARMv7架構的Cortex系列大受歡迎,並且已經創建起完善的開發者生態。所以新架構(指ARMv8)不能推倒重來另起爐竈,必須兼容ARMv7架構中的主要特性。app

爲了兼容32位程序,新架構將運行狀態分紅AArch64和AArch32兩種狀態,這兩種狀態的具體情形見下表:dom

AArch64: 64bit的運行狀態。地址存儲在64bit的寄存器中,使用A64指令集,可使用64bit寄存器
  提供3164位的通用寄存器(命名爲X0-X30,能夠經過W0~W30來訪問低32位。X30通常用做程序連接寄存器);
  一個64位的程序計數器(PC);
  64位的堆棧指針SP(每一個異常等級一個);
  以及64位的異常連接寄存器ELR(每一個異常等級一個),存儲了從中斷返回的地址
  提供32128位的NEON浮點寄存器(命令爲V0-V31),支持SIMD的向量運算以及標量浮點的運算
  使用A64指令集,A64指令集使用固定長度的指令,指令使用32位編碼
  從新定義了異常模型,該模型定義了4個異常等級EL0-EL3,異常等級提供了執行權限的等級制度,層級越高,權限越大。
  支持64位的虛擬地址空間
  定義了一組Process state寄存器PSTATENZCV/DAIF/CurrentEL/SPSel等),用於保存PE當前的狀態信息,A64指令集提供專門的指令來訪問這些PSTATE
  每一個系統寄存器(System register)名稱後面都帶有一個後綴,該後綴表示能夠訪問該寄存器的最低EL級別
AArch32: 32bit的運行狀態。地址存儲在32bit的寄存器中,可使用T32或者A32指令,使用32bit的寄存器
  提供1332位的通用寄存器(R0-R12;
  一個32位的程序計數器PC;
  一個32位的堆棧指針寄存器SP;
  一個32位的程序連接寄存器LRLR不只做爲程序連接器也用做異常連接寄存器ELR
  提供一個ELR,用來保存Hyp模式下異常返回的地址,Hyp模式與虛擬化有關。
  提供3264位的浮點寄存器,支持Advanced SIMD的向量計算以及標量浮點運算
  使用A32(使用固定長度指令,指令32位編碼)或者T32(使用變長指令,指令編碼能夠是16位的也能夠是32位的)指令集
  支持ARMv7架構中基於PE模式(FIQ/IRQ/Abort/Undefined/Svc)的異常模型。本質上是將PE模式映射到ARMv8架構中基於異常等級的異常模型中來。
  支持32位的虛擬地址空間
  定義了一組Process state寄存器PSTATENZCV/DAIF/CurrentEL/SPSel等),用於保存PE當前的狀態信息,A32/T32指令集提供專門的指令來訪問這些PSTATE。指令能夠經過APSR(Application Program Status Register)或者CPSRCurrent Program Status Register)來訪問PSTATE,就像在ARMv7架構中作的那樣。

                                                            表1:AArch64與AArch32的對比ide

1.3異常等級

ARMv7及以前的架構,爲PE定義了不少種模式:System/User/FIQ/Svc/Abort/IRQ/Undefined,每種模式都有本身的R13(用做SP)與R14(用做LR),FIQ模式還有本身的R8-R12。應用程序大部分時間跑在User模式下。這種設計,一方面是權限隔離,不一樣的模式有不一樣的資源訪問權限;另外一方面,FIQ/Abort/IRQ/Undefined這幾種模式與異常或者中斷關聯,處理相應的異常時迅速切換到對應的模式提升了處理效率。

到了ARMv8,ARM摒棄了這種多模式的設計,從新定義了一套異常或者說等級模型:

  1. 該模型定義了4個異常等級EL0-EL4,層級越高,特權越大,EL0爲非特權等級。
  2. 在進入異常時會把異常返回地址寫入到異常連接寄存器ELR中。
  3. 異常能夠在同異常等級中被處理,也能夠上升到更高的異常等級中被處理。EL1/EL2/EL3有各自不一樣的異常向量基址寄存器VBAR(Vector Base Address Register)。
  4. Syndrome register提供了異常的詳細信息,包括異常的類別、指令的長度、指令的具體信息

異常等級也能夠理解爲特權級別(Privilege Level)。特權級別劃分出來了,那麼在每一個級別,均可以幹那些事?ARM對這4個等級的設想能夠描述以下:

                             

                                                                          圖1:四個異常等級的實施例

  1. EL0:用來跑應用程序,包括虛擬化中客戶操做的程序;Secure狀態的App也是運行在該層
  2. EL1:用來跑操做系統的內核,包括虛擬化中的客戶操做系統;Secure狀態下的安全操做系統也是運行在該層
  3. EL2:用來支持虛擬化的Hypervisro運行在該層
  4. EL3:用來支持Security的Secure Monitor運行在該層

在實現架構時(ARM只是出設計,具體的芯片由廠商實現),並非全部的EL都要實現,其中EL0-EL1是必需要實現的,EL2主要是爲了支持虛擬化,能夠不用實現,EL3用來支持Security,也能夠不實現。

AArch32運行狀態兼容ARMv7中的多模式PE,是經過將不一樣的PE模式映射到不一樣的EL來實現的。

若是換作你,如何在ARMv8架構的等級模型上構建Linux操做系統呢?Linux操做系統只用到兩個特權級別:用戶空間程序與操做系統內核,用戶空間的程序通常運行在非特權級別,操做系統內核運行在特權級別,用戶空間發起的系統調用,以及觸發的異常與中斷,都須要交給「具備特權」的內核空間來處理。所以,針對ARMv8架構,用戶空間能夠落實到EL0層,內核空間能夠落實到EL1層。

1.4 AArch32與AArch64之間的關聯:

爲了向下兼容,ARM v8a將運行狀態分紅AArch64和AArch32兩種狀態。AArch64就是64位指令集的運行態,而AArch32是兼容Arm-v7a的狀態,全部Arm-v7a以及更早的軟件均可以在這個狀態上正常運行。對於應用程序來講,因爲EL0沒有權限進行AArch64和AArch32狀態切換的,所以只能一條道走到黑地用一種態。這也是AArch64與AArch32使用各自獨立指令集的緣由,兩種狀態下的指令集保持着井水不犯河水的「剋制」。但兩者在同一個系統中能夠共生,不免眉來眼去:

  1. AArch32與AArch64之間的切換隻能發生在進入異常或者從異常返回時。提升異常等級時不能同時下降寄存器的寬度,反之亦然。沒法經過分支跳轉或者連接寄存器(在舊架構中能夠經過BX指令實現ARM與Thumb狀態的轉換)來實施AArch32與AArch64之間的切換。
  2. AArch64狀態下的操做系統內核之上能夠跑AArch64的程序,也能夠跑AArch32的程序,反之不行;
  3. AArch64 Hypervisor之上能夠跑AArch64的guest OS,也能夠跑AArch32的guest OS,反之不行;
  4. 容許AArch32 Secure與 AArch64 Non-secure的組合(當前的手機都是雙系統,Android跑在AArch64 Non-secure一側,支付等跑在Secure一側)。
  5. 寄存器的映射關係以下:

                               

                                                 圖2:AArch32狀態的下用到的寄存器是用AArch64中的寄存器來模擬的

2.地址映射(虛擬地址映射爲物理地址)

2.1 地址映射與EL

地址映射就是把PE發出的虛擬地址映射爲內存的物理地址的過程。將虛擬地址映射爲物理地址,顯然這是一件須要「特權」才能乾的事(出於安全考慮,不能在非特權級去執行,否則,那些壞人想攻破你的系統不要太容易),須要與特權等級也就是異常等級聯繫起來:

  1. EL0之上的異常等級有本身的地址映射context,包括:地址換換表基址寄存器(TTBR)、地址映射控制寄存器(TCR)以及異常syndrome(寄存器)。
  2. EL0的地址映射由EL1來負責。
  3. 在Non-Secure狀態下,EL2能夠爲EL1/EL0新增一階轉換。新增的這一階地址映射被稱做Stage 2地址映射,相應的EL1/EL0中那階轉換被稱做Stage1地址映射。EL2是爲了支持虛擬化,Secure狀態下沒用EL2這一層;在Linux上可能也沒用這一層。

異常等級及其支持的地址映射以下圖所示:

圖3:stage1/stage2地址映射與異常等級的關係

安全與非安全是一種「物理隔離」。當PE運行在安全模式下,它能夠訪問那些標記爲安全的內存(表示這些內存只能在安全模式下訪問),也能夠訪問那些標記爲非安全的內存。當PE運行在非安全模式,它只能訪問那些標記爲非安全的內存,而沒法訪問那些標記爲安全的物理內存,從而實現物理隔離。

只有在非安全狀態下,從EL1/EL0提高至EL2等級時,Stage2的地址映射纔會開啓。若是Stage2沒有開啓,那麼Stage1從虛擬地址VA轉換後獲得的地址就是物理地址PA。而若是Stage2開啓,那麼Stage1從虛擬地址VA轉換後獲得的地址被稱爲「中間態」物理地址IPA,Stage2再將IPA轉換爲物理地址PA。

地址映射就是將虛擬地址映射爲物理地址,Stage1就夠了,爲什在EL2層又加上Stage2呢?咱們能夠這樣理解:

  1. 在Stage1,OS把VA轉換爲它本身認爲的PA。這個過程當中使用的是由OS管理的頁表。若是沒有虛擬化,則Stage 1轉換獲得PA就能夠直接訪問物理內存。
  2. Stage 2在虛擬系統(Hypervisor管理多個Guest OS)裏有效。能夠理解爲,Hypervisor把Guest OS認爲的「PA」進一步轉化爲真正的PA。這個過程使用的是另外一套由Hypervisor維護的頁表。在虛擬化環境中,只有通過Stage 2轉換後獲得的真正的PA才能訪問內存。

在後面的討論中,咱們認爲只進行stage 1的地址映射。

2.2地址空間與TTBR

64位系統能夠支持更多的虛擬地址空間了,那麼須要將虛擬地址直接提升到64位嗎?一方面,虛擬地址空間越大,一次地址映射(TTW,translation table walk)就須要更多級(Level)的查找。好比在32位系統中,若是page的尺寸爲4K,可能經過兩級查找(目錄表查找/頁表查找)就能將一個32位的虛擬地址映射爲對應的物理地址;若是虛擬地址提升到64位,則須要超過4級的查找才能完成一次地址映射,致使地址映射的開銷增長。另外一方面,當前也沒有那麼大的物理內存,用40位的物理地址就能覆蓋1024G的物理內存了,已經足能夠應付當前及將來很長一段時間的需求了。

所以,ARMv8的地址映射基於ARMv7的LAPE機制,LAPE的設計之初是要解決在如何在32位系統給程序提供超過4G內存空間的問題。LAPE使用48位虛擬地址,而且能夠將虛擬內存映射到40位物理內存地址上。每一個TTBR(地址映射表基址寄存器)最多可支持48位的虛擬地址空間,具體的位數在運行時設定。在一次TTW過程當中,有多少個級別的查找,取決於使用多少位的虛擬地址。

若是地址映射只支持一個VA區間(TCR_ELx 中若是隻有T0SZ域有效,說明地址映射只支持一個虛擬地址空間),則TTBR_ELx指向轉換表的基址,且L0(第一級)查找必須使用該轉換表。VA區間的虛擬地址位數=64-TTBR_ELx.T0SZE。

若是地址映射支持兩個虛擬地址區間(TCR_ELx 中T0SZ域與T1SZ域都有效),則每一個虛擬地址區間對應一個TTBR_ELx寄存器,其中TTBR0_ELx指向第一VA區域(起始地址爲0x0000000000000000)的轉換表的基址,第一VA區域的虛擬地址位數=64-TCR_ELx.T0SZ;TTBR1_ELx指向第二個VA區域(截止地址爲0xFFFFFFFFFFFFFFFF)的轉換表的基址,第二個VA區域的虛擬地址位數=64-TCR_ELx.T1SZ

下圖展現了EL1/EL0 Stage1的地址映射,該轉換支持兩個VA區域,虛擬地址位數爲48,各個VA區域的地址空間範圍如圖中所示:

圖4:當地址映射支持兩個VA區域時每一個VA區域的分佈狀況

  1. 兩個區域分佈在整個64區域的兩端,區域起始是固定的,區域的大小能夠單獨指定,每一個區域有本身專屬的TTBR。在上圖中,每一個區域都覆蓋了2^48-1 的地址空間
  2. 兩個區域之間的區域沒有映射,訪問這裏的地址會踩到雷——觸發異常。
  3. 在linux系統中,能夠將高端地址給內核使用,低端地址給用戶空間使用。

虛擬地址的位數肯定了爲48位,那麼咱們要爲48位的虛擬地址從新建立一種數據類型嗎?徹底不必,咱們用現成的64位整數來存儲地址,只是在進行地址映射時,從64位中取出「有效地址位」即低48位做爲虛擬地址。高16位徹底能夠拿來「變廢爲寶」,記錄一些額外的信息。

2.3轉換粒度與4級查找

虛擬地址的位數定下來了,那麼地址映射過程當中要通過幾級查找,才能把虛擬地址對應的物理地址找到呢?這與地址映射粒度有關。轉換粒度由兩個含義:一是page的大小,二是查找表的大小即表中包含多少個表項。page大小與查找表之間有什麼關聯呢?爲了提升效率,通常將查找表的尺寸與page對齊,這樣一個page就能容納一張查找表了。查找表的尺寸肯定了,表中每一項的尺寸是固定的(爲64位),那麼整個查找表的大小就能夠肯定了。

ARMv8架構支持3種地址映射粒度,以下表:

 

4KB granule

16KB granule

64KB granule

查找表的大小(最多能夠容納的條目數)

512

2048

8192

每級查找消耗虛擬地址中的位數

9-bit

11-bit

13-bit

頁內偏移offset

VA[11,0]

VA[13,0]

VA[15,0]

支持幾級查找

4級

4級

3級

TCR_ELx.TG0域(共2bit)的取值

00b

10b

01b

表2:3種轉換粒度對比表

咱們以4KB的粒度來講明上表中各項的含義。

  1. 在4KB粒度中,page的大小爲4K,每一個查找表的表項爲8byte,4KB一共能夠容納512個表項(512*8=4096)。所以查找表的大小爲512。
  2. 512=2^9,所以用9bit便可以覆蓋整個查找表,因此每級別查找表消耗(或者說解析)虛擬地址中的9個bit。
  3. 經過多個級別(好比4個級別)的查找,咱們找到了對應的物理內存在哪一個page上,即容該物理內存的page的基址,那麼最終的物理地址=page的基址+業內偏移。這個業內偏移就是虛擬地中,用查表以後剩餘的未消耗的bit來表示。頁的大小爲4K,那麼頁的基址(這裏已是物理地址了哇)確定是4K對齊的,即地址的低12爲0(12bit能表示的最大範圍是2^12-1 ,即4K-1,若是地址按4K對齊,低12位確定爲0,從13位開始的地址纔是「有效」的),那麼虛擬地址中的低12位即VA[11,0]就是用來表示頁內偏移的。
  4. 虛擬地址中的最低幾位是用來表示頁內偏移的,那麼其他的位段就是用來查表的,已知每級查找消耗的bit數,共有幾級查找就很明瞭。對4KB的粒度來講,支持(48 – 12) / 9 = 4級查找。當轉換粒度提升時,所需的查找級數(或者次數)就變少了,使得地址映射過程變得更平坦。平坦的含義是,以前須要4級查找,如今只須要兩級了,把4層的樓房拍扁變成兩層,可不就是更平坦了嘛。
  5. 轉換粒度由TCR_ELx寄存器的TG0域來指定,參考上表中的定義。

如無特殊聲明,後文中咱們討論的地址映射是以4KB爲粒度的。4KB轉換粒度下的4級查找,以下圖所示:

圖5:查找粒度爲4K時4級查找過程

咱們歷經4級查找,最終目的是爲了找到容納目標物理內存的物理頁的起始地址。物理內存是以頁爲單位組織起來的,所以,要找物理內存,先找到它所在的頁。這種內存管理方式被稱做頁式內存管理。

如今咱們思考一個問題,爲何要建立這種層級查找結構,爲何不乾脆搞一張超大的頁表,表裏的每一項指向一個頁面的基址。這樣只須要查一次表就能夠完成地址映射了,整個結構更扁平了,以下圖示意。

                                

圖6:一種設想的只須要一級查找的地址映射過程

這種架構至少存在下面幾個問題:

首先,Page Table的大小問題。爲了完成一次就能找到的目標,Page Table確定是個線性表(不能玩hash表那一套),用索引作下標直接能夠找到對應的項,而咱們又是拿虛擬地址去索引這張表,所以虛擬地址覆蓋多少個Page,Page Table中就應該有多少項。所以在32位系統中就有 4G/4K=1M個Page。因此Page Table中要有1048576個表項,每一個表項4byte,則Page Table就要消耗掉4M的空間。若是實際的物理內存只有512M,那Page Table中的不少項就會白白浪費。而在4級查找中,經過4個層級的查找表,咱們創建起了一個查找樹(或者目錄樹),整棵查找樹是動態造成的。一個進程不可能用光全部的虛存空間,虛存空間中確定有不一樣大小級別的空洞,在樹形結構中,對於空洞咱們能夠不建立對應的查找表從而節省空間。下圖展現了AArch64的4級查找表構成的查找樹。L0 Table中的一個表項領銜512G的地址空間,若是進程用不到這塊地址空間,那麼以該表項爲根,衍生的1個L1 Table以及512個L2 Table,以及由512個L2 Table衍生的512*512個L3 Table都不會被建立。一樣,L1 Table的一個表項領銜1G的地址空間,若是該1G空間不會被用到,那麼由該表項衍生的全部查找表都不會被建立。

圖7:4個層級組成的查找樹

其次咱們要考慮cache命中率的問題。因爲那張Page Table太大了,cache每次只能裝載其中的一部分。因爲地址的跳躍,致使Page Table的索引跳躍,致使以前加載到cache中內容失效,須要從新裝載cache。而在4級查找結構中,每一個查找表的size與Page size對齊,提升了cache的命中率。

綜合這兩點,咱們獲得結論:頁式內存管理是綜合考慮空間與效率的結果。

2.4內存block

經過前面的介紹,咱們已經看到了,4KB的轉換粒度須要4個級別的查找(因此才用translation table walk來描述地址映射過程,轉換過程就像在一條分叉衆多的路上行走同樣,每到一個路口,就須要拿出你的指南針,去找到前進的方向),這條路徑並不短。有沒有能夠提早結束查找的方法呢?

ARM在內存頁以外,提出內存block,一個block包含多個連續的頁,一個塊能夠覆蓋的空間更大。若是咱們咱們知道物理內存在某個塊中,並且知道了相對於塊基址的偏移,那麼咱們只須要找到這個塊的基址,再加上塊內偏移,即可以獲得物理內存的地址,從而提早就是查找過程。所以查找表中,可能存在在兩種表項:

  1. 一種表項指向下一級轉換表的基址,就像上圖展現的同樣。這種表項記做table描述符
  2. 另外一種指向一個物理塊的基址,虛擬地址中剩餘的位段做爲塊內偏移,如此即可以愉快地獲取物理地址,提早結束查找之旅。這種表項記做block描述符

4KB轉換粒度下的4級查找能夠進化到這種樣子:

圖8:考慮內存block後,4KB轉換粒度下的4級查找

在上圖中咱們也看到,在4KB的轉換粒度下的4級查找中,只有L1 Table以及L2 Table中能夠出現block描述符。L1 Table中的一個block描述符領銜1GB的地址空間(由於剩下的位段爲IA[29:0]共30bit,能夠覆蓋1G的空間),T2 Table中的block描述符領銜2MB的地址空間(由於剩下的位段爲IA[20:0]共21bit,能夠覆蓋2MB的空間)。block描述符與轉換粒度的關係見下表:

轉換粒度

block描述符

4KB

L0 不支持block描述符

L1 支持block描述符,一個block領銜1GB的地址空間

L2 支持block描述符,一個block領銜2MB的地址空間

16KB

L0/L1不支持block描述符

L2 支持block描述符,一個block領銜32MB的地址空間

64KB

不支持L0級別的查找(只有三級查找:L1-L3)

若是ARMv8.2-LPA功能沒開啓的話,L1不支持BLOCK

L2支持block描述符,一個block領銜512MB的地址空間

表3:3種轉換粒度下block描述符對比

請你們思考下,當轉換粒度爲4K時,爲何L0不支持block呢?

由於L0若是支持block的話,一個block領銜512G空間。一方面當前Android手機的內存最大才12G,上哪去找一塊連續的大小爲512G的物理內存呢?另外一方面block太大,也缺乏一些必要的靈活性。

上表中並無說L4是否支持block,要不要支持呢?答案是不須要支持。L4中維護的已是頁表了,在L4這個級別,即便支持block,block的尺寸已經與page一致了,固然就不必畫蛇添足再支持block了。

2.5查找表中的描述符

L0-L3查找表中的描述符格式:

前文說過,L0-L3的轉換表中的描述符可能存在兩種狀況:table描述符與block描述符。下圖列出轉換粒度爲4KB時,L0-L3的描述符格式:

圖9:4KB轉換粒度下的L0-L2對應的描述符格式

咱們拿64位的整數來表示地址,前文提到過,有效的虛擬地址只有48位,所以其餘的位段能夠挪做他用。因爲轉換表的尺寸與page對齊,轉換表的基址與4K對齊,因此48位的基址中低12位確定爲0,這低12位也可拿來作文章。這裏用bit[1:0]來指示該描述符的類型,以下表:

Bit[1:0]取值

描述符類型

‘x0’

無效描述符

‘01’

block描述符,output address指向一個block的基址

‘11’

Table描述符,next-level table address指向下一級轉換表的基址

表4:L0-L2描述符類型

在block描述符中,bit[63:52]與bit[11:2]這兩個位段用來描述block屬性(後文討論這些屬性)。

在table描述符中,bit[63:59]用來表示與table有關的屬性,詳細以下表:

標誌位

描述

NSTable

NS表示Non-Secure,該table描述符指向的下一級的轉換表是否存儲在secure memory之中。

爲0表示存儲在Secutre  PA中,不然表示存儲在Non-secure PA中。

當在non-secure狀態下進行地址映射時,該標誌別忽略

APTable

AP表示Access Permission,這兩個標誌爲指明瞭下一級轉換表的訪問權限,有以下4中狀況:

‘00’——不作限制

‘01’——EL0沒有權限訪問

‘10’——任何異常等級都不能寫(修改轉換表)

‘11’——任何異常等級都不能寫(修改轉換表);並且EL0不能讀

UXNTable /XNTable

當地址映射支持兩個VA區域時,名稱爲UXNTable,當地址映射只支持一個VA區域時,名稱爲XNTable。

UXN 的含義是「Unprivileged Execute Never」,XN的含義是「Execute Never」。它們用來表示內存的執行權限。好比咱們在加載動態庫時,動態庫的代碼段所在的地址就具備執行(x)權限。若是標誌位爲0表示具備執行權限,不然沒有執行權限。

PXNTable

該標誌只有當地址映射支持兩個VA區域時有效。PXN表示Privileged  Execute  Never,表示內存是否具備「特權執行」權限,好比內核的代碼便具備「特權執行」的權限。

表5:Table描述符中的標誌位

L3頁描述符格式:

L3查找表即頁表,4KB轉換粒度下,頁表的描述符格式以下:

圖10:4KB轉換粒度下的L3頁描述符格式

頁描述符與block描述符中的屬性字段:

從前文可知,block描述符與page描述符都有兩個屬性位段,主要的屬性字段以下:

 

圖11:block描述符與table描述符中的屬性位段

這些屬性的含義以下表:

屬性位段

描述

Contiguous

該表項是否爲連續表項中的一項。即轉換表在該表項先後是連續的,沒有空洞。這樣,這些連續的表項便有可能一次性加載到cache中(好比由一個TLB entry緩存)

nG

not Global

若是該表項緩存在TLB中,該標誌位指示TLB entry是全局有效仍是僅僅對當前進程有效

AF

Access Flag

當該標誌爲0,標明對應的內存區域(一個block或者一個page)是第一次訪問

SH

Shareability Field

參考後文

AP[2:1]

Access Permission.

control the stage 1 data access permissions, and:

AP[2] Selects between read-only and read/write access.

AP[1] Selects between Application level (EL0) control and the higher Exception level control.

This provides four permission settings for data accesses:

• Read-only at all levels.

• Read/write at all levels.

• Read-only at the higher Exception level, no access by software executing at EL0.

• Read/write at the higher Exception level, no access by software executing at EL0.

the following talbe shows the meaning of the AP[2:1] field for stage 1 of a translation regime that applies to both EL0 and a higher Exception level. In this table, an entry of None indicates that any access from that Exception level faults.

AP[2:1]

Access from higher Exception level

Access from EL0

00

Read/write

None

01

Read/write

Read/write

10

Read-only

None

11

Read-only

Read-only

 

NS

Non-secure

當從Secure狀態訪問內存時該標誌指示轉換後的地址在Secure區域仍是在Non-Secure區域

AttrIndx

指向內存區域的類型以及可緩存性。

參考後文

表6:block描述符與table描述符中的屬性字段的含義

ARMv8中定義了以下兩種內存類型(在ARM手冊中,類型是從內存模型的角度來描述,這裏摘自網絡定義)

類型

特性

Normal

(普通)

。讀寫通過Cache

。支持亂序,內存訪問順序同編程順序可能不一致

。支持預讀取

。支持內存非對齊訪問

Device

(設備)

。讀寫不通過Cache

。不支持亂序內存訪問

。不支持預讀取

。不支持內存非對齊訪問

Device類型的內存還有三個屬性,分別用G/R/E來表示,它們的定義是:

  1. Gathering (G/nG)
    - Determines whether multiple accesses can be merged into a single bus transaction
    - nG: Number/size of accesses on the bus = number/size of accesses in code
  2. Reordering (R/nR)
    - Determines whether accesses to the same device can be reordered
    - nR: Accesses to the same IMPLEMENTATION DEFINED block size will appear on the bus in program order
  3. Early Write Acknowledgement (E/nE)
    - Indicates to the memory system whether a buffer can send acknowledgements
    - nE: The response should come from the end slave, not buffering in the interconnect

表7:內存類型分爲normal與device

除了類型外,ARM還定義了其餘內存特性,它們的含義以下:

Shareability

(可共享性)

指當前內存頁表項的數據是否能夠同步到其它CPU上,多核CPU調用帶有該屬性頁表項的數據,一旦某個CPU修改了數據,那麼系統將自動更新到其它CPU的數據拷貝,實現內存數據一致性.

對於Normal類型的內存,有3種狀況:

• Inner Shareable, meaning it applies across the Inner Shareable shareability domain.

• Outer Shareable, meaning it applies across both the Inner Shareable and the Outer Shareable shareability

domains.

• Non-shareable.

Cacheability

(可緩存性)

指當前內存頁表項對於的數據是否能夠加載到Cache當中

對於Normal類型的內存,有3種狀況:

• Write-Through Cacheable.

• Write-Back Cacheable.

• Non-cacheable.

表8:內存的可共享性與可緩存性

3總結

本文首先介紹了ARMv8的歷史背景,瞭解了ARMv8主要的發力點:一是爲了支持更大的地址空間,ARMv8被設計成64爲的架構,而且從新設計的指令集T64;二是爲了應對企業級(服務器)的市場,提供對虛擬化的支持;三是對安全的加強。

爲了兼容舊的架構,ARMv8定義了兩種運行狀態:AARch64與AArch32,在AArch64狀態下運行64位指令,在AArch32狀態下運行32位的ARM指令或者16位的Thumb指令。

ARMv7及以前的架構爲處理器設置了7種模式(mode),並在此基礎上搭建了ARM的異常處理架構。ARMv8摒棄了這種設計, 它定義了4個異常等級(簡稱EL),異常等級構成了一個特權的等級制度,ARMv8在此基礎上構建新的異常處理架構。CPU廠家在實現本身的CPU時,只有EL0與EL1是必選項,EL2(主要用來支持虛擬化)與EL3(主要用來支持安全)是可選項。

當EL2存在時,ARM能夠提供兩個stage的地址映射。不過EL2主要用做虛擬化,手機上的CPU能夠不實現。這個時候只須要通過一個stage的地址映射,就能將虛擬地址(VA)轉換成物理地址(PA)。一個stage 的地址映射能夠支持兩個VA區域,好比第一VA區域(起始地址爲0x0000000000000000)給用戶空間使用,第二個VA區域(截止地址爲0xFFFFFFFFFFFFFFFF)給內核使用。

ARMv8虛擬內存架構發展自ARMv7的LAPE機制,支持48位虛擬地址。不過最終虛擬地址的寬度能夠經過TCR_ELxT0SZ域或者T1SZ域(對於支持兩個VA區域的地址映射來講)來指定。虛擬地址的寬度定好後,虛擬地址的空間大小便肯定下來了。

ARMv8支持3種轉換粒度(經過TCR_ELx寄存器的TG0域來指定):4KB、16KB與64KB。轉換粒度不只定義了page的size,也定義了查找表的尺寸。當轉換粒度爲4KB時,一個stage的地址映射支持4個level的查找(這與linux的4級內存管理暗合),這四個level分別記做L0~L3。這樣,一個虛擬地址便被分紅了5部分:L0 Index、L1 Index、L2 Index、L3 Index與offset。在查找時,拿Lx Index在Lx Table中查表(就是將Lx Index做爲索引,拿Lx Table的基址 + Lx Index),獲得下一級查找表的基址。L0 Table的基址存儲在寄存器TTBR中。L3 Table是一個頁表PT,頁表中每一個表項指向一個物理page的起始地址,當經過4級查找以後,就能夠找到虛擬地址對應的物理地址。

爲了加快地址映射過程,當轉換粒度爲4KB時,能夠在L1 Table或者L2 Table中插入一些block描述符,block表示匹配到一塊連續的物理內存。在地址映射過程當中,若是虛擬地址與一個內存塊匹配,則能夠提早結束查找過程,從而提升地址映射效率。所以,在L0~L2 的Table中,存在兩種描述符:block描述符與table描述符。

咱們拿64位的整數來表示地址,L0~L3 Table中的描述符也是64位的,而有效的虛擬地址只有48位,所以描述符中的其餘的位段能夠挪做他用,好比用做標記或者用來指示轉換表以及內存的相關屬性。

相關文章
相關標籤/搜索