詳解 ARM64 內核中對 52 位虛擬地址的支持

當 64 位硬件變得可用之後,處理更大地址空間(大於 232 字節)的需求變得顯而易見。現如今一些公司已經提供 64TiB 或更大內存的服務器,x86_64 架構和 arm64 架構現在允許尋址的地址空間大於 248 字節(可以使用默認的 48 位地址支持)。

詳解 ARM64 內核中對 52 位虛擬地址的支持詳解 ARM64 內核中對 52 位虛擬地址的支持

x86_64 架構通過讓硬件和軟件啓用五級頁表以支持這些用例。它允許尋址的地址空間等於 257 字節(詳情見x86:在 4.12 內核中啓用 5 級頁表)。它突破了過去虛擬地址空間 128PiB 和物理地址空間 4PiB 的上限。

arm64 架構通過引入兩個新的體系結構 —— ARMv8.2 LVA(更大的虛擬尋址) 和 ARMv8.2 LPA(更大的物理地址尋址) —— 拓展來實現相同的功能。這允許使用 4PiB 的虛擬地址空間和 4PiB 的物理地址空間(即分別爲 252 位)。

隨着新的 arm64 CPU 中支持了 ARMv8.2 體系結構拓展,同時現在開源軟件也支持了這兩種新的硬件拓展。

 Linux 5.4 內核開始, arm64 架構中的 52 位(大)虛擬地址(VA)和物理地址(PA)得到支持。儘管內核文檔描述了這些特性和新的內核運行時對舊的 CPU(硬件層面不支持 52 位虛擬地址拓展)和新的 CPU(硬件層面支持 52 位虛擬地址拓展)的影響,但對普通用戶而言,理解這些並且如何 “選擇使用” 52 位的地址空間可能會很複雜。

因此,我會在本文中介紹下面這些比較新的概念:

1. 在增加了對這些功能的支持後,內核的內存佈局如何“翻轉”到 Arm64 架構

2. 對用戶態應用的影響,尤其是對提供調試支持的程序(例如:kexec-tools、 makedumpfile 和 crash-utility)

3. 如何通過指定大於 48 位的 mmap 參數,使用戶態應用“選擇”從 52 位地址空間接受 VA?

ARMv8.2 架構的 LVA 和 LPA 拓展

ARMv8.2 架構提供兩種重要的拓展:大虛擬尋址(LVA)和大物理尋址(LPA)。

當使用 64 KB 轉換粒度時,ARMv8.2-LVA 爲每個翻譯表基地址寄存器提供了一個更大的 52 位虛擬地址空間。

在 ARMv8.2-LVA 中允許:

當使用 64 KB 轉換粒度時,中間物理地址(IPA)和物理地址空間拓展爲 52 位。
如果使用 64 KB 轉換粒度來實現對 52 位物理地址的支持,那麼一級塊將會覆蓋 4TB 的地址空間。
需要注意的是這些特性僅在 AArch64 架構中支持。

目前下列的 Arm64 Cortex-A 處理器支持 ARMv8.2 拓展:

Cortex-A55
Cortex-A75
Cortex-A76
更多細節請參考 Armv8 架構參考手冊。

Arm64 的內核內存佈局

伴隨着 ARMv8.2 拓展增加了對 LVA 地址的支持(僅當以頁大小爲 64 KB 運行時可用),在第一級轉換中,描述符的數量會增加。

用戶地址將 63-48 位位置爲 0,然而內核地址將這些位設置爲 1。TTBRx 的選擇由虛擬地址的 63 位決定。swapper_pg_dir 僅包含內核(全局)映射,然而 pgd 僅包含用戶(非全局)的映射。swapper_pg_dir 地址會寫入 TTBR1,且永遠不會寫入 TTBR0。

頁面大小爲 64 KB 和三個級別的(具有 52 位硬件支持)的 AArch64 架構下 Linux 內存佈局如下:

詳解 ARM64 內核中對 52 位虛擬地址的支持詳解 ARM64 內核中對 52 位虛擬地址的支持

4 KB 頁面的轉換查詢表如下:>
詳解 ARM64 內核中對 52 位虛擬地址的支持詳解 ARM64 內核中對 52 位虛擬地址的支持

64 KB 頁面的轉換查詢表如下:

詳解 ARM64 內核中對 52 位虛擬地址的支持詳解 ARM64 內核中對 52 位虛擬地址的支持
詳解 ARM64 內核中對 52 位虛擬地址的支持詳解 ARM64 內核中對 52 位虛擬地址的支持

內核對 52 位虛擬地址的支持

因爲支持 LVA 的較新的內核應該可以在舊的 CPU(硬件不支持 LVA 拓展)和新的 CPU(硬件支持 LVA 拓展)上都正常運行,因此採用的設計方法是使用單個二進制文件來支持 52 位(如果硬件不支持該特性,則必須在剛開始啓動時能回退到 48 位)。也就是說,爲了滿足 52 位的虛擬地址以及固定大小的 PAGE_OFFSET,VMEMMAP 必須設置得足夠大。

這樣的設計方式要求內核爲了新的虛擬地址空間而支持下面的變量:

VA_BITS         常量       *最大的* 虛擬地址空間大小
vabits_actual   變量       *實際的* 虛擬地址空間大小
翻轉內核內存佈局

保持一個單一內核二進制文件的設計方法要求內核的 .text 位於高位地址中,因此它們對於 48/52 位虛擬地址是不變的。因爲內核地址檢測器(KASAN)區域僅佔整個內核虛擬地址空間的一小部分,因此對於 48 位或 52 位的虛擬地址空間,KASAN 區域的末尾也必須在內核虛擬地址空間的上半部分。(從 48 位切換到 52 位,KASAN 區域的末尾是不變的,且依賴於 ~0UL,而起始地址將“增長”到低位地址)

爲了優化 phys_to_virt() 和 virt_to_phys(),頁偏移量將被保持在 0xFFF0000000000000 (對應於 52 位),這消除了讀取額外變量的需求。在早期啓動時將會計算 physvirt 和 vmemmap 偏移量以啓用這個邏輯。

考慮下面的物理和虛擬 RAM 地址空間的轉換:

詳解 ARM64 內核中對 52 位虛擬地址的支持詳解 ARM64 內核中對 52 位虛擬地址的支持

對用於調試內核的用戶態程序的影響

有幾個用戶空間應用程序可以用於調試正在運行的/活動中的內核或者分析系統崩潰時的 vmcore 轉儲(例如確定內核奔潰的根本原因):kexec-tools、makedumpfile 和 crash-utility。

當用它們來調試 Arm64 內核時,因爲 Arm64 內核內存映射被“翻轉”,因此也會對它們產生影響。這些應用程序還需要遍歷轉換表以確定與虛擬地址相應的物理地址(類似於內核中的完成方式)。

相應地,在將“翻轉”引入內核內存映射之後,由於上游破壞了用戶態應用程序,因此必須對其進行修改。

我已經提議了對三個受影響的用戶態應用程序的修復;有一些已經被上游接受,但其他仍在等待中:

提議 makedumpfile 上游的修復
提議 kexec-tools 上游的修復
已接受的 crash-utility 的修復
除非在用戶空間應用程序進行了這些修改,否則它們將仍然無法調試運行/活動中的內核或分析系統崩潰時的 vmcore 轉儲。

52 位用戶態虛擬地址

爲了保持與依賴 ARMv8.0 虛擬地址空間的最大爲 48 位的用戶空間應用程序的兼容性,在默認情況下內核會將虛擬地址從 48 位範圍返回給用戶空間。

通過指定大於 48 位的 mmap 提示參數,用戶態程序可以“選擇”從 52 位空間接收虛擬地址。
例如:

.mmap_high_addr.c
----
   maybe_high_address = mmap(~0UL, size, prot, flags,...);

通過啓用以下的內核配置選項,還可以構建一個從 52 位空間返回地址的調試內核:

CONFIG_EXPERT=y && CONFIG_ARM64_FORCE_52BIT=y
結論

總結一下:

  • 內核版本從 5.14 開始,新的 Armv8.2 硬件拓展 LVA 和 LPA 在內核中得到良好支持。
  • 像 kexec-tools 和 makedumpfile 被用來調試內核的用戶態應用程序現在無法支持新拓展,仍在等待上游接受修補。
  • 過去的用戶態應用程序依賴於 Arm64 內核提供的 48 位虛擬地址將繼續原樣工作,而較新的用戶態應用程序通構指定超過 48 位更大的 mmap 提示參數來 “選擇加入”已接受來自 52 位的虛擬地址。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章