Linux中國

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

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

x86_64 架構通過讓硬體和軟體啟用五級頁表以支持這些用例。它允許定址的地址空間等於 2^57 位元組(詳情見 x86:在 4.12 內核中啟用 5 級頁表)。它突破了過去虛擬地址空間 128PiB 和物理地址空間 4PiB 的上限。

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

隨著新的 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 內存布局如下:

  開始                  結束                       大小          用途
  --------------------------------------------------------------------  0000000000000000      000fffffffffffff           4PB          用戶
  fff0000000000000      fff7ffffffffffff           2PB          內核邏輯內存映射
  fff8000000000000      fffd9fffffffffff        1440TB          [間隙]
  fffda00000000000      ffff9fffffffffff         512TB          Kasan 陰影區
  ffffa00000000000      ffffa00007ffffff         128MB          bpf jit 區域
  ffffa00008000000      ffffa0000fffffff         128MB          模塊
  ffffa00010000000      fffff81ffffeffff         ~88TB          vmalloc 區
  fffff81fffff0000      fffffc1ffe58ffff          ~3TB          [保護區域]
  fffffc1ffe590000      fffffc1ffe9fffff        4544KB          固定映射
  fffffc1ffea00000      fffffc1ffebfffff           2MB          [保護區域]
  fffffc1ffec00000      fffffc1fffbfffff          16MB          PCI I/O 空間
  fffffc1fffc00000      fffffc1fffdfffff           2MB          [保護區域]
  fffffc1fffe00000      ffffffffffdfffff        3968GB          vmemmap
  ffffffffffe00000      ffffffffffffffff           2MB          [保護區域]

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

  +--------+--------+--------+--------+--------+--------+--------+--------+
  |63    56|55    48|47    40|39    32|31    24|23    16|15     8|7      0|
  +--------+--------+--------+--------+--------+--------+--------+--------+
   |                 |         |         |         |         |
   |                 |         |         |         |         v
   |                 |         |         |         |   [11:0]  頁內偏移量
   |                 |         |         |         +-> [20:12] L3 索引
   |                 |         |         +-----------> [29:21] L2 索引
   |                 |         +---------------------> [38:30] L1 索引
   |                 +-------------------------------> [47:39] L0 索引
   +-------------------------------------------------> [63] TTBR0/1

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

  +--------+--------+--------+--------+--------+--------+--------+--------+
  |63    56|55    48|47    40|39    32|31    24|23    16|15     8|7      0|
  +--------+--------+--------+--------+--------+--------+--------+--------+
   |                 |    |               |              |
   |                 |    |               |              v
   |                 |    |               |            [15:0]  頁內偏移量
   |                 |    |               +----------> [28:16] L3 索引
   |                 |    +--------------------------> [41:29] L2 索引
   |                 +-------------------------------> [47:42] L1 索引 (48 位)
   |                                                   [51:42] L1 索引 (52 位)
   +-------------------------------------------------> [63] TTBR0/1

![](/data/attachment/album/202101/30/170549s2wlj8t3w0hbdlf6.png "arm64 Multi-level Translation")

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

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

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

VA_BITS         常量       *最大的* 虛擬地址空間大小

vabits_actual   變數       *實際的* 虛擬地址空間大小

因此,儘管 VA_BITS 設置了最大的虛擬地址空間大小,但實際上支持的虛擬地址空間大小由 vabits_actual 確定(具體取決於啟動時的切換)。

翻轉內核內存布局

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

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

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

/*
 * 內核線性地址開始於虛擬地址空間的底部
 * 測試區域開始處的最高位已經是一個足夠的檢查,並且避免了擔心標籤的麻煩
 */

#define virt_to_phys(addr) ({                                   
        if (!(((u64)addr) & BIT(vabits_actual - 1)))            
                (((addr) & ~PAGE_OFFSET) + PHYS_OFFSET)
})

#define phys_to_virt(addr) ((unsigned long)((addr) - PHYS_OFFSET) | PAGE_OFFSET)

在上面的代碼中:
 PAGE_OFFSET — 線性映射的虛擬地址的起始位置位於 TTBR1 地址空間
 PHYS_OFFSET — 物理地址的起始位置以及 vabits_actual — *實際的*虛擬地址空間大小

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

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

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

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

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

除非在用戶空間應用程序進行了這些修改,否則它們將仍然無法調試運行/活動中的內核或分析系統崩潰時的 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

請注意此選項僅用於調試應用程序,不應在實際生產中使用。

結論

總結一下:

  1. 內核版本從 5.14 開始,新的 Armv8.2 硬體拓展 LVA 和 LPA 在內核中得到良好支持。
  2. 像 kexec-tools 和 makedumpfile 被用來調試內核的用戶態應用程序現在無法支持新拓展,仍在等待上游接受修補。
  3. 過去的用戶態應用程序依賴於 Arm64 內核提供的 48 位虛擬地址將繼續原樣工作,而較新的用戶態應用程序通構指定超過 48 位更大的 mmap 提示參數來 「選擇加入」已接受來自 52 位的虛擬地址。

這篇文章參考了 AArch64 架構下的 Linux 內存布局Linux 5.9.12 內核文檔。它們均為 GPLv2.0 許可。

via: https://opensource.com/article/20/12/52-bit-arm64-kernel

作者:Bhupesh Sharma 選題:lujun9972 譯者:萌新阿岩 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的電子郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國