@mengxinayan
This commit is contained in:
Xingyu Wang 2021-01-30 17:05:23 +08:00
parent e6728409f9
commit f19901f79b

View File

@ -1,43 +1,45 @@
[#]: collector: (lujun9972) [#]: collector: (lujun9972)
[#]: translator: (mengxinayan) [#]: translator: (mengxinayan)
[#]: reviewer: ( ) [#]: reviewer: (wxy)
[#]: publisher: ( ) [#]: publisher: ( )
[#]: url: ( ) [#]: url: ( )
[#]: subject: (Understanding 52-bit virtual address support in the Arm64 kernel) [#]: subject: (Understanding 52-bit virtual address support in the Arm64 kernel)
[#]: via: (https://opensource.com/article/20/12/52-bit-arm64-kernel) [#]: via: (https://opensource.com/article/20/12/52-bit-arm64-kernel)
[#]: author: (Bhupesh Sharma https://opensource.com/users/bhsharma) [#]: author: (Bhupesh Sharma https://opensource.com/users/bhsharma)
理解 ARM64 内核中对 52 比特虚拟地址的支持 理解 ARM64 内核中对 52 虚拟地址的支持
====== ======
随着 64 比特硬件的普及,增加了处理更大地址空间的需求。
![拼图碎片聚在一起形成计算机屏幕][1]
当 64 比特硬件变得可用之后处理更大地址空间大于232字节的需求变得显而易见。现如今一些公司已经提供 64TiB 或更大内存的服务器x86_64 架构和 arm64 架构现在允许寻址的地址空间大于 248 字节(可以使用默认的 48 比特地址支持) > 随着 64 位硬件的引入,增加了处理更大地址空间的需求。
x86_64 架构通过让硬件和软件支持五级页表以支持这些用例。它允许寻址的地址空间等于 257 字节(详情见 [x86在 4.12 内核中启用 5 级页表][2])。它突破了过去虚拟地址空间 128PiB 和 物理地址空间 4PiB 的上限。 ![](https://img.linux.net.cn/data/attachment/album/202101/30/170448rynbm9b6nmb90403.jpg)
arm64 架构通过引入两个新的体系结构拓展来实现相同的功能—ARMv8.2 LVA更大的虚拟寻址 和 ARMv8.2 LPA更大的物理地址寻址。这允许使用 4PiB 的虚拟地址空间和 4PiB 的物理地址空间(即分别为 252 比特)。 当 64 位硬件变得可用之后,处理更大地址空间(大于 232 字节)的需求变得显而易见。现如今一些公司已经提供 64TiB 或更大内存的服务器x86_64 架构和 arm64 架构现在允许寻址的地址空间大于 248 字节(可以使用默认的 48 位地址支持)。
在新的 arm64 CPU 中已经支持了 ARMv8.2 体系结构拓展,同时现在开源软件也支持了这两种新的硬件拓展 x86_64 架构通过让硬件和软件启用五级页表以支持这些用例。它允许寻址的地址空间等于 257 字节(详情见 [x86在 4.12 内核中启用 5 级页表][2])。它突破了过去虚拟地址空间 128PiB 和物理地址空间 4PiB 的上限
从 5.4 内核开始, arm64 架构中的52 比特虚拟地址VA和物理地址PA得到支持。尽管[内核文档][3]描述了这些特性和新的内核运行时对旧的 CPU硬件层面不支持 52 比特虚拟地址拓展)和新的 CPU硬件层面支持 52 比特虚拟地址拓展的影响但对普通用户而言理解这些并且如何“选择使用”52比特的地址空间可能会很复杂。 arm64 架构通过引入两个新的体系结构 —— ARMv8.2 LVA更大的虚拟寻址 和 ARMv8.2 LPA更大的物理地址寻址 —— 拓展来实现相同的功能。这允许使用 4PiB 的虚拟地址空间和 4PiB 的物理地址空间(即分别为 252 位)。
随着新的 arm64 CPU 中支持了 ARMv8.2 体系结构拓展,同时现在开源软件也支持了这两种新的硬件拓展。
从 Linux 5.4 内核开始, arm64 架构中的 52 位虚拟地址VA和物理地址PA得到支持。尽管[内核文档][3]描述了这些特性和新的内核运行时对旧的 CPU硬件层面不支持 52 位虚拟地址拓展)和新的 CPU硬件层面支持 52 位虚拟地址拓展)的影响,但对普通用户而言,理解这些并且如何 “选择使用” 52 位的地址空间可能会很复杂。
因此,我会在本文中介绍下面这些比较新的概念: 因此,我会在本文中介绍下面这些比较新的概念:
1. 在增加了对这些功能的支持后,内核的内存布局如何“翻转”到 Arm64 架构 1. 在增加了对这些功能的支持后,内核的内存布局如何“翻转”到 Arm64 架构
2. 对用户态应用的影响尤其是对提供调试支持的程序例如kexec-tools, makedumpfile 和 crash-utility 2. 对用户态应用的影响尤其是对提供调试支持的程序例如kexec-tools makedumpfile 和 crash-utility
3. 如何通过指定大于 48 比特的 mmap 参数,使用户态应用“选择”接收 52 比特地址 3. 如何通过指定大于 48 位的 mmap 参数,使用户态应用“选择”从 52 位地址空间接受 VA
### ARMv8.2 架构的 LVA 和 LPA 拓展 ### ARMv8.2 架构的 LVA 和 LPA 拓展
ARMv8.2 架构提供两种重要的拓展:更大的虚拟地址LVA和更大的物理地LPA ARMv8.2 架构提供两种重要的拓展:大虚拟寻址LVA和大物理寻LPA
当使用 64 KB 转换粒度时ARMv8.2-LVA 为每个基地址寄存器提供了一个更大的 52 比特虚拟地址空间。 当使用 64 KB 转换粒度时ARMv8.2-LVA 为每个翻译表基地址寄存器提供了一个更大的 52 位虚拟地址空间。
在 ARMv8.2-LVA 中包含 在 ARMv8.2-LVA 中允许
* 当使用 64 KB 转换粒度时中间物理地址IPA和物理地址空间拓展为 52 比特 * 当使用 64 KB 转换粒度时中间物理地址IPA和物理地址空间拓展为 52
* 如果使用 64 KB 转换粒度来实现对 52 比特物理地址的支持,那么一级块将会覆盖 4TB 的地址空间。 * 如果使用 64 KB 转换粒度来实现对 52 物理地址的支持,那么一级块将会覆盖 4TB 的地址空间。
_需要注意的是这些特性仅在 AArch64 架构中支持。_ _需要注意的是这些特性仅在 AArch64 架构中支持。_
@ -49,13 +51,13 @@ _需要注意的是这些特性仅在 AArch64 架构中支持。_
更多细节请参考 [Armv8 架构参考手册][4]。 更多细节请参考 [Armv8 架构参考手册][4]。
### Arm64 的内核内存布局 ### Arm64 的内核内存布局
伴随着 ARMv8.2 拓展增加了对 LVA 地址的支持(仅当页大小为 64 KB 是可用),在第一级翻译中,描述符的数量会增加。 伴随着 ARMv8.2 拓展增加了对 LVA 地址的支持(仅当以页大小为 64 KB 运行时可用),在第一级转换中,描述符的数量会增加。
用户地址将 63-48 比特位置为 0然而内核地址将这些比特位置为 1。TTBRx 选择由虚拟地址的 63 比特位决定。`swapper_pg_dir` 仅包含内核全局映射,然而 `pgd` 仅包含用户(非全局)的映射。`swapper_pg_dir` 地址会写入 TTBR1 且永远不会写入 TTBR0。 用户地址将 63-48 位位置为 0然而内核地址将这些位设置为 1。TTBRx 的选择由虚拟地址的 63 位决定。`swapper_pg_dir` 仅包含内核(全局)映射,然而 `pgd` 仅包含用户(非全局)的映射。`swapper_pg_dir` 地址会写入 TTBR1且永远不会写入 TTBR0。
**页面大小为 64 KB 和三个级别的(具有 52 比特硬件支持)的 AArch64 架构下 Linux 内存布局如下:** **页面大小为 64 KB 和三个级别的(具有 52 硬件支持)的 AArch64 架构下 Linux 内存布局如下:**
``` ```
开始 结束 大小 用途 开始 结束 大小 用途
@ -85,11 +87,11 @@ _需要注意的是这些特性仅在 AArch64 架构中支持。_
   |                 |         |         |         |         |    |                 |         |         |         |         |
   |                 |         |         |         |         v    |                 |         |         |         |         v
| | | | | [11:0] 页内偏移量 | | | | | [11:0] 页内偏移量
| | | | +-> [20:12] L3 索引 | | | | +-> [20:12] L3 索引
| | | +-----------> [29:21] L2 索引 | | | +-----------> [29:21] L2 索引
   |                 |         +---------------------> [38:30] L1 索引    |                 |         +---------------------> [38:30] L1 索引
   |                 +-------------------------------> [47:39] L0 索引    |                 +-------------------------------> [47:39] L0 索引
   +-------------------------------------------------> [63] TTBR0/1    +-------------------------------------------------> [63] TTBR0/1
``` ```
**64 KB 页面的转换查询表如下:** **64 KB 页面的转换查询表如下:**
@ -101,18 +103,18 @@ _需要注意的是这些特性仅在 AArch64 架构中支持。_
   |                 |    |               |              |    |                 |    |               |              |
   |                 |    |               |              v    |                 |    |               |              v
   |                 |    |               |            [15:0]  页内偏移量    |                 |    |               |            [15:0]  页内偏移量
   |                 |    |               +----------> [28:16] L3 索引    |                 |    |               +----------> [28:16] L3 索引
   |                 |    +--------------------------> [41:29] L2 索引    |                 |    +--------------------------> [41:29] L2 索引
| +-------------------------------> [47:42] L1 索引 (48 比特) | +-------------------------------> [47:42] L1 索引 (48 位)
| [51:42] L1 索引 (52 比特) | [51:42] L1 索引 (52 )
   +-------------------------------------------------> [63] TTBR0/1    +-------------------------------------------------> [63] TTBR0/1
``` ```
![][5] ![][5]
### 内核对 52 比特虚拟地址的支持 ### 内核对 52 虚拟地址的支持
因为支持 LVA 的较新的内核应在旧的CPU硬件不支持 LVA 拓展和新的CPU硬件支持 LVA 拓展)上都可以正常运行,因此采用的设计方法是使用单个二进制文件来支持 52 比特(如果硬件不支持该特性,则必须在刚开始启动时能回到 48 比特)。也就是说,为了满足 52 比特的虚拟地址以及固定大小的 `PAGE_OFFSET`VMEMMAP 必须设置得足够大。 因为支持 LVA 的较新的内核应该可以在旧的 CPU硬件不支持 LVA 拓展)和新的 CPU硬件支持 LVA 拓展)上都正常运行,因此采用的设计方法是使用单个二进制文件来支持 52 位(如果硬件不支持该特性,则必须在刚开始启动时能回退到 48 位)。也就是说,为了满足 52 位的虚拟地址以及固定大小的 `PAGE_OFFSET``VMEMMAP` 必须设置得足够大。
这样的设计方式要求内核为了新的虚拟地址空间而支持下面的变量: 这样的设计方式要求内核为了新的虚拟地址空间而支持下面的变量:
@ -122,13 +124,13 @@ VA_BITS 常量 *最大的* 虚拟地址空间大小
vabits_actual 变量 *实际的* 虚拟地址空间大小 vabits_actual 变量 *实际的* 虚拟地址空间大小
``` ```
因此,尽管 `VA_BITS` 设置了最大的虚拟地址空间大小,但实际上支持的虚拟地址空间大小由 `vabits_actual` 确定(具体取决于启动时的切换) 因此,尽管 `VA_BITS` 设置了最大的虚拟地址空间大小,但实际上支持的虚拟地址空间大小由 `vabits_actual` 确定(具体取决于启动时的切换)
#### 翻转内核内存布局 #### 翻转内核内存布局
保持一个单内核二进制文件的设计方法要求内核的 .text 文件位于高位地址中,因此它们对于 48/52 比特虚拟地址都不变。因为内核地址检测器KASAN区域仅占整个内核虚拟地址空间的一小部分因此对于 48 比特或 52 比特的虚拟地址空间KASAN 区域的末尾也必须在内核虚拟地址空间的上半部分。(从 48 比特切换到 52 比特KASAN 区域的末尾是不变的且依赖于 `~0UL`,而起始地址将“增长”到低位地址) 保持一个单内核二进制文件的设计方法要求内核的 `.text` 位于高位地址中,因此它们对于 48/52 位虚拟地址是不变的。因为内核地址检测器KASAN区域仅占整个内核虚拟地址空间的一小部分因此对于 48 位或 52 位的虚拟地址空间KASAN 区域的末尾也必须在内核虚拟地址空间的上半部分。(从 48 位切换到 52 位KASAN 区域的末尾是不变的,且依赖于 `~0UL`,而起始地址将“增长”到低位地址)
为了优化 `phys_to_virt()``virt_to_phys()`,页偏移量将被保持在 `0xFFF0000000000000` (对应于 52 比特),这消除了读取额外变量的需求。在早期启动时将会计算 `physvirt``vmemmap` 偏移量以启用这个逻辑。 为了优化 `phys_to_virt()``virt_to_phys()`,页偏移量将被保持在 `0xFFF0000000000000` (对应于 52 ),这消除了读取额外变量的需求。在早期启动时将会计算 `physvirt``vmemmap` 偏移量以启用这个逻辑。
考虑下面的物理和虚拟 RAM 地址空间的转换: 考虑下面的物理和虚拟 RAM 地址空间的转换:
@ -139,8 +141,8 @@ vabits_actual 变量 *实际的* 虚拟地址空间大小
*/ */
#define virt_to_phys(addr) ({ \ #define virt_to_phys(addr) ({ \
if (!(((u64)addr) & BIT(vabits_actual - 1))) \ if (!(((u64)addr) & BIT(vabits_actual - 1))) \
(((addr) & ~PAGE_OFFSET) + PHYS_OFFSET) (((addr) & ~PAGE_OFFSET) + PHYS_OFFSET)
}) })
#define phys_to_virt(addr) ((unsigned long)((addr) - PHYS_OFFSET) | PAGE_OFFSET) #define phys_to_virt(addr) ((unsigned long)((addr) - PHYS_OFFSET) | PAGE_OFFSET)
@ -152,11 +154,11 @@ vabits_actual 变量 *实际的* 虚拟地址空间大小
### 对用于调试内核的用户态程序的影响 ### 对用于调试内核的用户态程序的影响
一些用户空间应用程序用于调试正在运行的/活动中的内核或者分析系统崩溃时的 vmcore 转储例如确定内核奔溃的根本原因kexec-tools, makedumpfile, 和 crash-utility。 有几个用户空间应用程序可以用于调试正在运行的/活动中的内核或者分析系统崩溃时的 vmcore 转储例如确定内核奔溃的根本原因kexec-tools、makedumpfile 和 crash-utility。
当用它们来调试 Arm64 内核时,因为 Arm64 内核内存映射被“翻转”,因此也会对它们产生影响。这些应用程序还需要遍历转换表以确定与虚拟地址相应的物理地址(类似于内核中的完成方式)。 当用它们来调试 Arm64 内核时,因为 Arm64 内核内存映射被“翻转”,因此也会对它们产生影响。这些应用程序还需要遍历转换表以确定与虚拟地址相应的物理地址(类似于内核中的完成方式)。
相应地,在将“翻转”引入内核内存映射之后,由于上游中断了用户态应用程序,因此必须对其进行修改。 相应地,在将“翻转”引入内核内存映射之后,由于上游破坏了用户态应用程序,因此必须对其进行修改。
我已经提议了对三个受影响的用户态应用程序的修复;有一些已经被上游接受,但其他仍在等待中: 我已经提议了对三个受影响的用户态应用程序的修复;有一些已经被上游接受,但其他仍在等待中:
@ -166,25 +168,25 @@ vabits_actual 变量 *实际的* 虚拟地址空间大小
除非在用户空间应用程序进行了这些修改,否则它们将仍然无法调试运行/活动中的内核或分析系统崩溃时的 vmcore 转储。 除非在用户空间应用程序进行了这些修改,否则它们将仍然无法调试运行/活动中的内核或分析系统崩溃时的 vmcore 转储。
### 52 比特用户态虚拟地址 ### 52 用户态虚拟地址
为了保持与依赖 ARMv8.0 虚拟地址空间的最大为 48 比特的用户空间应用程序的兼容性,在默认情况下内核会将虚拟地址从 48 比特范围返回给用户空间。 为了保持与依赖 ARMv8.0 虚拟地址空间的最大为 48 位的用户空间应用程序的兼容性,在默认情况下内核会将虚拟地址从 48 位范围返回给用户空间。
通过指定大于48位的mmap提示参数用户态程序可以“选择”从 52 比特空间接收虚拟地址。 通过指定大于 48 位的 mmap 提示参数,用户态程序可以“选择”从 52 空间接收虚拟地址。
例如: 例如:
``` ```
.mmap_high_addr.c .mmap_high_addr.c
\---- ----
   maybe_high_address = mmap(~0UL, size, prot, flags,...);    maybe_high_address = mmap(~0UL, size, prot, flags,...);
``` ```
通过启用以下的内核配置选项,还可以构建一个从 52 比特空间返回地址的调试内核: 通过启用以下的内核配置选项,还可以构建一个从 52 空间返回地址的调试内核:
``` ```
`   CONFIG_EXPERT=y && CONFIG_ARM64_FORCE_52BIT=y`    CONFIG_EXPERT=y && CONFIG_ARM64_FORCE_52BIT=y
``` ```
_请注意此选项仅用于调试应用程序不应在实际生产中使用。_ _请注意此选项仅用于调试应用程序不应在实际生产中使用。_
@ -193,9 +195,9 @@ _请注意此选项仅用于调试应用程序不应在实际生产中使用
总结一下: 总结一下:
1. 内核版本从 5.14 开始,新的 Armv8.2 硬件拓展 LVA 和 LPA 在内核中得到很好的拓展 1. 内核版本从 5.14 开始,新的 Armv8.2 硬件拓展 LVA 和 LPA 在内核中得到良好支持
2. 像 kexec-tools 和 makedumpfile 被用来调试内核的用户态应用程序现在无法支持新拓展,仍在等待上游接受修补。 2. 像 kexec-tools 和 makedumpfile 被用来调试内核的用户态应用程序现在无法支持新拓展,仍在等待上游接受修补。
3. 过去的用户态应用程序依赖于 Arm64 内核提供的 48 比特虚拟地址将继续原样工作,而较新的用户态应用程序通构指定超过 48 比特更大的 mmap 提示参数来 “选择加入”已接受来自 52 比特的虚拟地址。 3. 过去的用户态应用程序依赖于 Arm64 内核提供的 48 位虚拟地址将继续原样工作,而较新的用户态应用程序通构指定超过 48 位更大的 mmap 提示参数来 “选择加入”已接受来自 52 位的虚拟地址。
* * * * * *
@ -208,7 +210,7 @@ via: https://opensource.com/article/20/12/52-bit-arm64-kernel
作者:[Bhupesh Sharma][a] 作者:[Bhupesh Sharma][a]
选题:[lujun9972][b] 选题:[lujun9972][b]
译者:[萌新阿岩](https://github.com/mengxinayan) 译者:[萌新阿岩](https://github.com/mengxinayan)
校对:[校对者ID](https://github.com/校对者ID) 校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出