11

为 ARMv7 (Cortex-a8) 编译的 Linux 内核 2.6.34.3

我查看了内核代码,看起来 Linux 内核在 TTB1(转换表库)上为内核地址空间(0xC0000000 以上的所有内容)和 ttb0(0xC0000000 以下的所有内容)上的用户进程设置了硬件页表,每次更改进程上下文切换。它是否正确?我仍然很困惑 MMU 如何知道要查看哪个 ttb 进行翻译?

我读到 TTBCR(转换表基本控制寄存器)确定在未找到 MVA 时要遍历哪个 ttb 寄存器,但是该寄存器始终读取 0,这意味着始终使用 ARM 架构参考手册中的 TTBR0。这怎么可能?谁能向我解释一下 Linux 内核是如何使用这两个 ttb 的?

我从这个站点https://www.cs.rutgers.edu/~pxk/416/notes/10-paging.html阅读了 ttb 的工作原理,但我仍然不明白内核如何使用这两个 ttb

(仔细检查内核代码,由于某种原因,ttb0 和 ttb1 都已设置,但似乎从未使用过 ttb1,我将 TTB1 寄存器设置为 0,Linux 内核继续照常运行)

4

4 回答 4

17

TTBR 寄存器一起用于确定完整的 32 位或 40 位地址空间的寻址。哪个寄存器用于哪个地址范围由 TTBCR 中的 tXsz 位控制。有一个对应于 TTBR0 的 t0sz 条目和对应于 TTBR1 的 t1sz 条目。

每个 TTBRx 寄存器寻址的页表是独立的,但您通常会发现大多数 Linux 实现只使用 TTBR0。Linux 期望能够使用 ARM 不支持的 3G/1G 地址空间分区方案。如果您查看 ARMv7 架构参考手册的第 B3-1345 页,您会看到 t0sz 和 t1sz 的值分别决定了 TTBR0 和 TTBR1 支持的地址范围。为了使迷失方向更加混乱,甚至有可能在 TTBR0 和 TTBR1 支持的范围不连续的情况下出现脱节的地址空间,从而导致系统地址空间出现漏洞。美好的时光!

不过,要回答您的主要问题,ARM 建议使用 TTBR0 将偏移量存储到 USER 进程使用的页表中,并使用 TTBR1 将偏移量存储到 KERNEL 使用的页表中。我还没有看到一个真正做到这一点的实现。在所有情况下几乎都只使用 TTBR0,而 TTBR1 包含 L1 表的副本。

那么这是如何工作的呢?TTBR 的值作为进程状态的一部分存储,并且在每次进程关闭时简单地恢复。这就是预期的工作方式。最初,TTBR1 将保持内核表的恒定值并且永远不会被替换或换出,而 TTBR0 将在每次进程之间的上下文切换时更改。显然,大多数用于 ARM 的 Linux 实现已经决定基本上消除对 TTBR1 的使用,并坚持使用 TTBR0。

如果你想在你的设备上测试这个理论,试着敲击 TTBR1 并观察什么都没有发生。然后尝试敲击 TTBR0 并观察您的系统崩溃。我还没有遇到一个没有产生完全相同结果的实例。长话短说,TTBR1 在 Linux 中是无用的,而 TTBR0 几乎完全被使用,只是被换掉了。

现在,一旦您获得 LPAE 支持,就扔掉所有这些,重新开始。在这个实现中,您将开始看到 t0sz 和 t1sz 的值不是零,因此也不是零。

于 2013-05-21T22:35:34.977 回答
1

我对 ARM 架构知之甚少,但从我在您随附的链接中读到的内容,我猜 Linux 以这种方式实现其虚拟内存管理:

虚拟地址的高位决定使用哪一个。表的基址存储在两个基址寄存器之一(TTBR0 或 TTBR1)中,具体取决于虚拟地址的最高 n 位是否为 0(使用 TTBR0)或不(使用 TTBR1)。n 的值由转换表基本控制寄存器 (TTBCR) 定义。

该寄存器TTBCR告诉哪些地址将从TTBR0或指向的页表中转换TTBR1。如果TTBCR包含0xc000000,则从0to0xbfffffff的任何地址都由 指向的页表翻译,而从to的TTBR0任何地址都由 指向的页表翻译。这与用户进程 3GB/内核 1GB 的 Linux 内存分割相匹配。0xc00000000xffffffffTTBR1

这允许一个设计,其中操作系统和内存映射 I/O 位于地址空间的上部并由 TTBR1 中的页表管理,而用户进程位于内存的下部并由TTB0 中的页表。在上下文切换时,操作系统必须更改 TTBR0 以指向新进程的第一级表。TTBR1仍将包含操作系统的内存映射和内存映射 I/O。

因此, 的值TTBR1永远不会改变,因为您希望内核被永久映射(想想当一个中断被引发时会发生什么)。另一方面,TTBR0在每次进程切换时修改,它包含当前进程的页表。

于 2013-01-22T17:05:10.440 回答
1

请参阅http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211k/Bihgfcgf.html

对于 ARM5 及更低版本,TTB 表的大小和对齐方式是固定的(至 16k)。每个级别 1 条目代表 1MB。表项为 32bits (16k*1M/(32bit/8) = 4GB)。TTBCR控制TTBR0表大小。从上面的网址,

选择使用哪个转换表基址寄存器
转换表基址寄存器的选择如下:
如果 N = 0,则始终使用转换表基址寄存器 0。
- 这是复位时的默认情况。它向后兼容 ARMv5 或更早的处理器。
如果 N 大于 0,则:
- 如果虚拟地址的位 [31:32-N] 全部为 0,则使用转换表基址寄存器 0,否则使用转换表基址寄存器 1。

所以TTBR0的大小也设置了内存分割。对于传统的Linux 3G/1G 1G/3G,应该选择值2 。4kB 表 == 1G 内存 == 位 31..30 为零。对于6的值,该表是 256byte == 64MB == 位 31..26 为零。

在 Linux 用语中,这些是页面全局条目(这会拆分此页面全局目录)。这些条目可以指向另一个表或只是一个 1MB。下一个表条目是页面中间Linux 目录,然后是最后的页表条目。我认为页面中间条目在 ARM 上未使用。

MMU 硬件并非每次都走在桌子上。有一个 TLB(翻译后备缓冲区)。它就像 MMU 表的缓存。当操作系统更新这些表时,必须刷新 TLB,否则处理器将使用过时的条目。同样,ARM 缓存是虚拟标记的,因此更改映射也可能意味着必须刷新缓存。由于这些原因,您永远不想在上下文切换时更改内容。共享库文本(比如libc.so)在上下文切换时应该相同。希望每个进程都有 libc.so 映射到相同的虚拟地址。这样做有很大的收获;较低的内存使用和良好的 I-cache 使用。

PID寄存器以及主管/用户模式也可以控制内存访问。这些是可以在上下文切换上切换的单个寄存器。

有关ARMV5 上的 PID 和域使用的信息,请参阅http://lwn.net/images/conf/rtlws11/papers/proc/p01.pdf 。当前的 Linux 源代码与论文描述的不完全一样。Linux 完全有可能不需要使用这种机制并将TTBCR设置为零,以便 ARM 子架构的 VM 代码类似。

编辑:相信TTBCR功能可用于实现 3G/1G 拆分。我认为 Rutger 的页面是在泛泛地讨论TTBCR,而不是在Linux上下文中。此外,至少2.6.38 Linux 使用DACR,但不使用pidfcse,因为它支持有限数量的进程。

http://lwn.net/Articles/106177/ - 在罗格斯页面上也有引用。

于 2013-01-22T20:21:52.583 回答
0

TTBR0 保存转换表 0 的基地址,以及它所占用的内存信息。

这是从 Hyp 模式以外的模式进行内存访问的阶段 1 转换的转换表之一

于 2018-09-02T07:44:16.693 回答