本文是對learn_the_architecture_-_aarch64_memory_management的部分翻譯和個人注解。個人英文水平有限,若有翻譯不當,歡迎加我私人微信LinuxDriverDev與我交流。
總覽
本文介紹了AAR64內(nèi)存管理中最重要的內(nèi)容--內(nèi)存轉(zhuǎn)換,解釋了虛擬地址是如何翻譯為物理地址的,翻譯表的格式,以及如何管理TLBS。
什么是內(nèi)存管理
內(nèi)存管理描述了如何控制操作性系統(tǒng)對內(nèi)存的訪問。每次操作系統(tǒng)或應(yīng)用程序訪問內(nèi)存時,硬件都會進行內(nèi)存管理。內(nèi)存管理是一種給應(yīng)用程序動態(tài)分配內(nèi)存區(qū)域的方法。
處理器是用來運行復雜系統(tǒng)的,比如Linux 支持虛擬內(nèi)存系統(tǒng)。軟件在操作系統(tǒng)上運行只能看到虛擬地址,而處理器負責把虛擬地址轉(zhuǎn)換為物理地址。這些物理地址最終都會被內(nèi)存系統(tǒng)轉(zhuǎn)換為實際的物理位置。
虛擬地址和物理地址
使用虛擬地址的好處是它允許對軟件進行管理,比如操作系統(tǒng)可以控制內(nèi)存以什么樣的方式呈現(xiàn)給應(yīng)用程序。操作系統(tǒng)可以控制那些內(nèi)存是可見的,控制該內(nèi)存可見的虛擬地址以及允許對該內(nèi)存那些區(qū)域進行訪問。這就可以實現(xiàn)操作系統(tǒng)對應(yīng)用程序的沙箱管理(對另一個應(yīng)用程序隱藏一個應(yīng)用程序的資源),并且提供對底層的硬件抽象。
使用虛擬地址的另一個好處是操作系統(tǒng)可以將多個零散的物理內(nèi)存區(qū)域組織為單個連續(xù)的虛擬地址空間呈現(xiàn)給應(yīng)用程序。
虛擬地址也有利于軟件開發(fā)人員,軟件開發(fā)人員編寫應(yīng)用程序時不需要關(guān)心物理內(nèi)存。應(yīng)用程序知道,物理內(nèi)存轉(zhuǎn)換為虛擬內(nèi)存由操作系統(tǒng)和硬件共同完成。
實際上,每個應(yīng)用程序都可以使用自己的一組虛擬地址。這些虛擬地址將映射到物理系統(tǒng)中的不同位置。當操作系統(tǒng)在不同的應(yīng)用程序之間切換的時候,它會重新組織物理地址和虛擬地址的映射關(guān)系。這就意味著不同的應(yīng)用程序都會映射到正確的物理位置。
虛擬地址通過映射關(guān)系轉(zhuǎn)換為物理地址。虛擬地址和物理地址之間的映射關(guān)系存儲在轉(zhuǎn)換表(有時稱為頁表)中,如下圖所示:
翻譯表在內(nèi)存中由操作系統(tǒng)或者hypervisor管理,轉(zhuǎn)換表不是靜態(tài)的,它可以隨著軟件變化的需要而更新表。因為不同軟件的物理地址是不同的,所以這就會改變虛擬地址和物理地址之間的映射關(guān)系。
內(nèi)存管理單元
內(nèi)存管理單元(MMU)負責把軟件使用的虛擬地址轉(zhuǎn)換為內(nèi)存系統(tǒng)使用的物理地址。MMU組成如下:
Table wake unit,從內(nèi)存中讀出翻譯表的邏輯。
TLBs,近期使用的翻譯表的緩存。
軟件使用的所有地址都是虛擬地址,這些地址會傳遞給MMU,MMU在TLB中檢查這些地址是否存在。如果在TLB中沒有找到,Table wake unit會從內(nèi)存中讀取適當?shù)膖able entry(一個或多個),如下所示:
在進行內(nèi)存訪問時,虛擬地址必須被轉(zhuǎn)換為物理地址。這種轉(zhuǎn)換需求也適用于緩存數(shù)據(jù),因為在 Armv6 和更高版本的處理器上,數(shù)據(jù)緩存(DCACHE)使用物理地址(物理標記的地址)存儲數(shù)據(jù)。因此,必須先翻譯地址,然后才能完成高速緩存查找。
Table entry
翻譯表的工作原理是把虛擬地址空間劃分為同等大小的塊。并且每個塊在表中提供一個entry。
表中的entry 0映射到塊0,entry1映射到塊1,依此類推。每個entry都包含對應(yīng)物理內(nèi)存塊的地址以及訪問物理地址時要使用的屬性。
Table lookup
當虛擬地址轉(zhuǎn)換為物理地址時,就會去查表,虛擬地址會被分為兩部分:
下圖展示了一級頁表查找過程。
圖中標有“which entry”的高位表明要查看那個塊的entry,在表中搜索時被用作索引。此entry是虛擬地址對應(yīng)的物理地址。圖中標記為“offset in block”的是該塊內(nèi)的偏移量,虛擬地址與物理地址的offset是一一對應(yīng)的,不會因為發(fā)生轉(zhuǎn)換而改變。
Multilevel translation
在單級lookup中,虛擬地址空間被分成大小相等的塊。而在實際中,使用最多的是多級頁表。
第一級頁表將虛擬地址劃分為大塊,表中的每個entry都可以指向一個相同大小的物理內(nèi)存塊,或者指向下一級頁表,下一級頁表中將表將細分更小的塊。我們稱這種類型的表為“多級頁表”。
下圖是一個三級頁表的例子:
在 Armv8-A 中,最大級別數(shù)為 4,級別編號為 0 到 3。這種多級方法允許使用更大的塊和更小的塊。大塊和小塊的特點如下:
與小塊相比,大塊在在轉(zhuǎn)譯時需要的級別更少,在TLB中緩存效率更高。
小塊為軟件提供了對內(nèi)存分配的細粒度控制。然而,小塊在 TLB 中的緩存效率較低。緩存效率較低是因為小塊需要通過多級進行多次讀取才能轉(zhuǎn)譯。
為了管理這種權(quán)衡,操作系統(tǒng)必須平衡使用大塊映射的效率與使用小塊映射的靈活性來獲得最佳性能。處理器在開始查表時不知道轉(zhuǎn)譯的大小,它通過執(zhí)行 table walk 計算出正在轉(zhuǎn)換的塊的大小。
地址空間
AArch64 中有幾個獨立的虛擬地址空間。此圖顯示了這些虛擬地址空間:
Non-secure EL0 and EL1
Non-secure EL2
EL3
這些虛擬地址空間中的每一個都是獨立的,并且有自己的setting和table。我們經(jīng)常將這些setting和table稱為“translation regimes”。Secure EL0、Secure EL1 和 Secure EL2 也有虛擬地址空間,但圖中未顯示。
因為有多個虛擬地址空間,所以指定一個地址在哪個地址空間很重要。例如NS.EL2:0x8000指的是Non-secure EL2虛擬地址空間中的地址0x8000。
該圖還表明NS-EL0 和NS-EL1 的虛擬地址會使用兩組table。這些table支持虛擬化并允許hypervisor將虛擬機看到的物理地址虛擬化。
Armv9-A 支持上述 Armv8-A 的所有虛擬地址空間。Armv9-A 引入了可選的RME。實現(xiàn)RME時,還存在其他轉(zhuǎn)譯機制:
Realm EL1和EL0
Realm EL2和EL0
Realm EL2
在虛擬化中,我們將操作系統(tǒng)控制的翻譯稱之為stage1,stage1將虛擬地址轉(zhuǎn)換為中間物理地址(IPA)。在stage1,操作系統(tǒng)認為 IPA 是物理地址空間。但是,hypervisor控制第二組轉(zhuǎn)換,我們稱之為stage2。stage2轉(zhuǎn)換將 IPA 轉(zhuǎn)換為物理地址。下圖顯示了兩組翻譯的工作原理:
雖然table格式有一些細微差別,但 Stage 1 和 Stage 2 翻譯的過程通常是相同的。
在Arm,我們在許多例子中都使用了0x8000這個地址。0x8000也是 用Arm鏈接器armlink進行鏈接的默認地址。這個地址來自于 早期的微型計算機,BBC Micro Model B,它的ROM的地址為 地址為0x8000。BBC Micro Model B是由一家名為 Acorn的公司,該公司開發(fā)了Acorn RISC Machine(ARM),后來成為Arm。
物理地址
除了多個虛擬地址空間,AArch64 還有多個物理地址空間(PAS):
Non-secure PAS0
Secure PAS
Realm PAS (Armv9-A only)
Root PAS (Armv9-A only)
虛擬地址可以映射到哪一個或哪幾個物理地址空間取決于 處理器的當前安全狀態(tài)。下面的列表顯示了安全狀態(tài)和其 對應(yīng)的虛擬地址映射目的地。
Non-secure state:虛擬地址只能映射到非安全的物理地址
Secure state:虛擬地址可以映射到安全的/非安全的物理地址
Realm state:虛擬地址可以映射到Realm 和非安全的物理地址
Root state:虛擬地址可以映射到任何物理地址
當處于具有多個物理地址空間可見性的安全狀態(tài)時,轉(zhuǎn)換表entry控制哪個物理地址空間被使用。下圖展示了多個物理地址空間的映射關(guān)系:
地址大小
AArch64 是 64 位架構(gòu),但這并不意味著所有地址都是 64 位的。虛擬地址以 64 位格式存儲。因此,加載指令 (LDR) 和存儲指令 (STR) 中的地址始終在由X 寄存器指定。但是,并非 X 寄存器中的所有地址都有效。下圖顯示了 AArch64 中虛擬地址空間的布局:
如左圖所示,EL0/EL1的虛擬地址空間有兩個區(qū)域:內(nèi)核空間和用戶空間。內(nèi)核空間在頂部,用戶空間在底部。內(nèi)核空間和用戶空間有各自單獨的轉(zhuǎn)換表。
如右圖所示,其他所有異常級別的地址空間在底部有一塊單獨的區(qū)域。
每個區(qū)域的地址空間的大小最多為52位。然而,每個區(qū)域都可以獨立地縮小。TCR_ELx寄存器中的TnSZ字段控制虛擬地址空間的大小。例如,該圖顯示TCR_EL1控制EL0/EL1 虛擬地址空間。
虛擬地址被設(shè)置為264-TCR_ELx.TnSZ。虛擬地址的大小也可以用地址位數(shù)來表示:64 - TnSZ。
因此,如果TCR_EL1.SZ1設(shè)置為32,則 EL0/EL1 虛擬地址空間中內(nèi)核區(qū)域的大小為 2的32次方個字節(jié)(0xFFFF_FFFF_0000_0000 到 0xFFFF_FFFF_FFFF_FFFF)。當訪問超出超出配置范圍的虛擬地址的空間時,會被當作翻譯錯誤。
這種配置的好處是我們只需要描述盡可能多的我們想要使用的地址空間,這樣可以節(jié)省時間和空間。
例如,假設(shè)操作系統(tǒng)內(nèi)核需要 1GB 的地址空間(30 位地址大小)作為其內(nèi)核空間。如果操作系統(tǒng)將 T1SZ 設(shè)置為 34,描述 1GB 的轉(zhuǎn)換表entry被創(chuàng)建,因為64 - 34 = 30。
物理地址大小
物理地址的大小是可以自定義的,最大可支持到52位。ID_AA64MMFR0_EL1 寄存器規(guī)定了處理器實現(xiàn)的大小。對于 Arm Cortex-A 處理器,這通常是 40 位或 44 位。
對于Armv8.0-A,物理地址最大為48位。Armv8.2-A中最大為52位。
中間物理地址(IPA)的大小
如果你在翻譯表項(translation table entry)中指定的輸出地址大于實際的最大值,內(nèi)存管理單元(MMU)將產(chǎn)生一個異常,即地址大小錯誤。IPA空間的大小可以用與虛擬地址空間相同的方式進行配置。
VTCR_EL2.T0SZ寄存器可以設(shè)置的最大值與處理器支持的物理地址大小相同。這意味著不能配置比支持的物理地址空間更大的 IPA 空間。
地址空間標識-標記進程
許多現(xiàn)代操作系統(tǒng)的應(yīng)用程序似乎都是在同一個地址區(qū)運行的,這就是我們所說的用戶空間。事實上,不同的應(yīng)用程序需要不同的映射。這意味著虛擬地址0X8000的轉(zhuǎn)換取決于當前運行的應(yīng)用程序。
理想情況下,我們希望不同應(yīng)用程序的轉(zhuǎn)譯能在轉(zhuǎn)譯緩沖區(qū)(TLB)內(nèi)共存。以防止在上下文切換時影響性能。但是處理器如何知道要使用哪個版本的 VA 0x8000 轉(zhuǎn)換呢?在 Armv8-A 中,答案是地址空間標識符 (ASID)。
對于EL0/EL1虛擬地址空間,可以使用轉(zhuǎn)譯表entry屬性字段中的nG位將轉(zhuǎn)譯標記為全局(G)或非全局(nG)。例如,內(nèi)核映射是全局轉(zhuǎn)譯,而應(yīng)用程序映射是非全局轉(zhuǎn)譯。全局轉(zhuǎn)譯適用于當前正在運行的任何應(yīng)用程序。非全局轉(zhuǎn)譯只適用于一個特定的應(yīng)用程序。
非全局映射在 TLB 中使用 ASID 進行標記。在TLB中搜索時,TLB entry 中的ASID 與當前選擇的 ASID 進行比較。如果它們不匹配,則不使用 TLB 條目。下圖顯示了內(nèi)核空間中沒有 ASID 標記的全局映射和用戶空間中具有 ASID 標記的非全局映射:
圖中顯示,多個應(yīng)用程序的TLB條目被允許在緩存中共存。而ASID決定使用哪個條目。
ASID被存儲在兩個TTBRn_EL1寄存器中的一個。通常,TTBR0_EL1用于用戶空間。因此,單個寄存器更新可以同時更改 ASID 和它指向的轉(zhuǎn)換表。
虛擬機標識-使用擁有的虛擬機來標記翻譯
EL0/EL1轉(zhuǎn)換也可以用虛擬機標識符(VMID)進行標記。VMID 允許來自不同 VM 的轉(zhuǎn)譯在緩存中共存。這類似于 ASID 為來自不同應(yīng)用程序的在翻譯工作的方式。實際上,這意味著某些轉(zhuǎn)換將同時使用 VMID 和 ASID 進行標記,并且兩者都必須匹配才能使用 TLB entry。
當安全狀態(tài)支持虛擬化時,EL0/EL1轉(zhuǎn)換總是被標記為VMID--即使Stage 2轉(zhuǎn)換沒有被啟用。這意味著,如果你正在編寫初始化代碼,并且沒有使用hypervisor,那么在設(shè)置Stage 1 MMU之前設(shè)置一個已知的VMID值是很重要的。
Common not Private
如果一個系統(tǒng)包括多個處理器,在一個處理器上使用的ASIDs和VMIDs在其他處理器上是否有相同的意義?
對于 Armv8.0-A,答案是它們的含義不一定相同。軟件不需要在多個處理器上以相同的方式使用給定的 ASID。例如,ASID 5 可能由一個處理器上的計算器和另一個處理器上的 Web 瀏覽器使用。這意味著由一個處理器創(chuàng)建的 TLB entry不能被另一個處理器使用。
實際上,軟件不太可能在處理器之間以不同的方式使用 ASID。軟件在給定系統(tǒng)中的所有處理器上以相同方式使用 ASID 和 VMID 更為常見。因此,Armv8.2-A 在轉(zhuǎn)換表基址寄存器 (TTBR) 中引入了 Common not Private (CnP) 位。當設(shè)置 CnP 位時,軟件承諾在所有處理器上以相同的方式使用 ASID 和 VMID,這允許由一個處理器創(chuàng)建的 TLB 條目被另一個處理器使用。
轉(zhuǎn)換表格式
在這里,我們可以看到翻譯表entry允許的不同格式。
每個entry是 64 位,低兩位確定entry的類型。
請注意,某些table entry僅在特定級別有效。table的最大級別數(shù)是四級,這就是為什么沒有l(wèi)evel3(或第四級)表的表描述符的原因。同樣地,第0級也沒有塊描述符或頁描述符。因為第0級 entry覆蓋了很大的虛擬地址空間區(qū)域,因此在level0允許塊是沒有意義的。
轉(zhuǎn)換粒度
轉(zhuǎn)換粒度是可以描述的最小的內(nèi)存塊。AArch64 支持三種不同的粒度大?。?KB、16KB 和 64KB。
處理器支持的粒度是自定義的并由 ID_AA64MMFR0_EL1 保存。所有Arm Cortex-A處理器都支持4KB和64KB。選擇的粒度是最高level table中描述的大小。
一個處理器所支持的粒度大小是ID_AA64MMFR0_EL1定義的,所有Arm Cortex-A處理器都支持4KB和64KB。選擇的顆粒是可以在最新級別表中描述的最小塊。也可以描述更大的塊。此表顯示了基于所選粒度的每個級別表的不同塊的尺寸:
在 Armv9.2-A 和 Armv8.7-A 推出之前,使用 52 位地址是有限制的。當所選顆粒為4KB或16KB時,最大虛擬地址大小為48位。同樣,輸出物理地址限制為 48 位。只有在使用 64KB 顆粒時,才能使用完整的 52 位。
地址轉(zhuǎn)換的初始level
虛擬地址空間的粒度和大小共同控制地址轉(zhuǎn)換的起始級別。
上表總結(jié)了每個級別表中每個粒度的塊大?。▎蝹€entry覆蓋的虛擬地址范圍的大小)。從塊的大小,你可以算出虛擬地址的哪些位是用來索引每一級表的。
讓我們以4KB的粒度為例。這張圖顯示了用于索引4KB粒度的 不同級別表的索引。
如圖所示,將TCR_ELx.T0SZ設(shè)置為32,以地址為單位的虛擬地址空間的大小計算方式如下:64 - T0SZ = 32-bit
從之前的4KB配置的粒度圖中可以看出,level0是47:39位索引的。這些在32位地址空間是不存在的。因此,地址翻譯是從level1開始的。
接著,假設(shè)設(shè)置T0SZ為34:64 - T0SZ = 30
這一次,不存在level0 和level1的索引。因此,該配置的轉(zhuǎn)譯起始級別是level2。
當虛擬地址空間的大小減少時,您需要更少級別的表來描述它。這些示例基于使用 4KB 粒度。當使用 16KB 和 64KB 顆粒時,同樣的原理也適用,但地址位會發(fā)生變化。
控制地址轉(zhuǎn)換的寄存器
地址轉(zhuǎn)換由系統(tǒng)寄存器組合來控制:
SCTLR_ELx
M:使能MMU
C:使能Dcache
TTBR0_ELx and TTBR1_ELx
BADDR:轉(zhuǎn)譯表物理地址/中間物理地址的起點
ASID:非全局轉(zhuǎn)譯的標識符
TCR_ELx
PS/IPS:物理地址/中間物理地址的大小。
TnSZ:轉(zhuǎn)譯表能表示的地址空間的大小
TGn:粒度大小
SH/IRGN/ORGN:緩存能力共享能力的使能
TBIn:禁止搜索table中的某一行
MAIR_ELx
Attr:statge1 的類型和緩存能力控制
關(guān)閉MMU
當MMU被禁用時,所有的地址都是平行映射的。即物理地址和虛擬地址是一一對應(yīng)的。
維護TLB
Translation Lookaside Buffers (TLB) 緩存最近使用的轉(zhuǎn)譯項。此緩存允許后續(xù)查找重復使用轉(zhuǎn)譯,而無需重新讀取table。
如果要更改轉(zhuǎn)換表entry或控制entry的解釋方式,則需要使 TLB 中受影響的entry無效。如果不使這些entry無效,則處理器可能會繼續(xù)使用舊的轉(zhuǎn)譯。
處理器不允許導致以下任何故障的翻譯緩存到 TLB:
翻譯錯誤(未映射的地址)
地址大小錯誤(超出地址范圍)
非法訪問
因此,在第一次映射地址時,你不需要讓TLB 無效。然而,如果要做以下操作,需要讓TLB失效:
Unmap an address:將有效的地址標記為無效
改變映射關(guān)系:將地址權(quán)限從只讀改為讀寫。
改變table 的轉(zhuǎn)譯方式:修改粒度大小時,table的轉(zhuǎn)譯方式也會改變。
TLB操作的格式
TLBI 指令用于使 TLB 中的entry無效。該指令的語法是
TLBI < type >< level >{IS|OS} {, < xt >}
< type >:指定無效的entries
All - 所有entries
VA - Entry matching VA and ASID in Xt
VAA - Entry matching VA in Xt, for any ASID
ASID - Any entry matching the ASID in Xt
< level >: 操作哪一個地址空間
E1 = EL0/1 virtual address space
E2 = EL2 virtual address space
E3 = EL3 virtual address space
< IS|OS >內(nèi)部共享還是外部共享
< Xt >:操作那個地址或者ASID
例如,一個正在更新其內(nèi)核轉(zhuǎn)換表中entry的操作系統(tǒng) (OS)。典型的 TLB 無效序列如下所示:
地址翻譯指令
地址轉(zhuǎn)換指令(AT)可以查詢特定地址的轉(zhuǎn)換。地址翻譯的結(jié)果,屬性會寫入物理寄存器PAR_EL1。
AT指令的語法具有優(yōu)先級。例如,EL2指令可以查詢EL0/EL1的翻譯表,但是EL1指令不能查詢EL2的。
總結(jié)
下面對本文內(nèi)容做個簡單總結(jié),以下這些問題是我們要明白的。
地址翻譯中,statge和level有什么區(qū)別?
statge 指的是把輸入地址轉(zhuǎn)換為輸出地址的過程。對于stage1,就是從VA到IPA的過程。stage2 從IPA到PA的過程。
level指的是翻譯給定階段的table,將一個大塊劃分為小塊的過程。
物理地址最大是多少?
物理地址大小由IMPLEMENTATION DEFINED定義,在ARMV8.2-A后為52位。
虛擬地址大小由哪些寄存器控制?
stage2的TCR_ELx.TnSZ, or VTCR_EL2.T0SZ
翻譯粒度是什么?支持的大小是多少?
翻譯粒度指的是內(nèi)存可以描述的最小的塊。支持4KB, 16KB, and 64KB.
TLBI ALLE3 作用是什么?
EL3虛擬地址中使所有的TLB entries無效。
發(fā)生翻譯錯誤時的翻譯表項能緩存在TLB中嗎?
不能
當MMU關(guān)閉時,地址是如何映射的?
平行映射,即輸入地址和輸出地址相同。
什么是ASID?什么時候TLB entry會包含ASID?
ASID是地址空間標識符,它標識了翻譯與那個應(yīng)用相關(guān)聯(lián)。非全局映射(nG=1)在在TLB中被標記為一個ASID。
更多信息可以來這里獲取==>>電子技術(shù)應(yīng)用-AET<<
電子技術(shù)應(yīng)用專欄作家:嵌入式與Linux那些事
原文鏈接:https://mp.weixin.qq.com/s/4b8sNRyN7QgfM_Nihzm6mQ