arm linux 怎么把内核空间的寄存器映射到用户 空间
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了arm linux 怎么把内核空间的寄存器映射到用户 空间相关的知识,希望对你有一定的参考价值。
如果是通过 alloc_page() 获得了高端内存对应的 page,如何给它找个线性空间?内核专门为此留出一块线性空间,从 PKMAP_BASE 到 FIXADDR_START ,用于映射高端内存。在 2.6内核上,这个地址范围是 4G-8M 到 4G-4M 之间。这个空间起叫”内核永久映射空间”或者”永久内核映射空间”。这个空间和其它空间使用同样的页目录表,对于内核来说,就是 swapper_pg_dir,对普通进程来说,通过 CR3 寄存器指向。通常情况下,这个空间是 4M 大小,因此仅仅需要一个页表即可,内核通过来 pkmap_page_table 寻找这个页表。通过 kmap(),可以把一个 page 映射到这个空间来。由于这个空间是 4M 大小,最多能同时映射 1024 个 page。因此,对于不使用的的 page,及应该时从这个空间释放掉(也就是解除映射关系),通过 kunmap() ,可以把一个 page 对应的线性地址从这个空间释放出来。 参考技术A 你在说啥?狗屎
Linux内核ARM转换表基础(TTB0和TTB1)
针对ARMv7编译的Linux内核2.6.34.3(Cortex-a8)
我查看了内核代码,看起来Linux内核为TTB1(转换表基础)上的内核地址空间(一切都超过0xC0000000)和ttb0上的用户进程(0xC0000000下的所有内容)设置了硬件页表,进程上下文切换。它是否正确?我仍然很困惑MMU如何知道翻译的哪个ttb?
我读到TTBCR(转换表基本控制寄存器)确定在找不到MVA时要走哪个ttb寄存器,但是寄存器总是读为0,这意味着始终在ARM体系结构参考手册中使用TTBR0。怎么可能?任何人都可以向我解释Linux内核如何使用这两个ttbs?
我读了ttb如何从这个站点https://www.cs.rutgers.edu/~pxk/416/notes/10-paging.html工作,但我仍然不明白内核如何使用这两个ttbs
(仔细检查了内核代码,由于某种原因ttb0和ttb1都已设置,但似乎从未使用过ttb1,我将TTB1寄存器设置为0并且Linux内核继续照常运行)
TTBR寄存器一起用于确定完整32位或40位地址空间的寻址。哪个寄存器用于通过TTBCR中的tXsz位控制的地址范围。对应于TTBR0的t0sz和TTBR1的t1sz有一个条目。
每个TTBRx寄存器寻址的页表是独立的,但您通常会发现大多数Linux实现只使用TTBR0。 Linux希望能够使用3G / 1G地址空间分区方案,ARM不支持该方案。如果查看“ARMv7体系结构参考手册”的B3-1345页,您会看到t0sz和t1sz的值分别决定了TTBR0和TTBR1支持的地址范围。为了增加对迷失方向的困惑,甚至可能有脱离的地址空间,其中TTBR0和TTBR1支持不连续的范围,从而导致系统地址空间中的漏洞。美好时光!
但是,为了回答您的主要问题,ARM建议使用TTBR0将偏移量存储到USER进程使用的页表中,并使用TTBR1将偏移量存储到KERNEL使用的页表中。我还没有看到实际执行此操作的单个实现。在所有情况下都使用TTBR0,TTBR1包含L1表的副本。
那么这是如何工作的呢? TTBR的值存储为过程状态的一部分,并且每次切换过程时都会简单地恢复。这就是它的工作方式。最初,TTBR1将为内核表保持一个常量值,永远不会被替换或换出,而每次在进程之间进行上下文切换时,TTBR0都会被更改。显然,ARM的大多数Linux实现都决定基本上消除TTBR1的使用并坚持使用TTBR0来实现一切。
如果你想在你的设备上测试这个理论,试试whacking TTBR1并注意什么都没发生。然后尝试敲击TTBR0并观察系统崩溃。我还没有遇到一个没有产生完全相同结果的实例。简而言之,TTBR1对于Linux来说是无用的,而且TTBR0几乎只用于交换。
现在,一旦你获得LPAE支持,抛弃所有这些并重新开始。这是一个实现,你将开始看到t0sz和t1sz的值不是零,因此也是N.
我对ARM体系结构知之甚少,但是从我在附带的链接中读到的内容,我想Linux实现了它的虚拟内存管理:
虚拟地址的高位确定使用哪一个。表的基数存储在两个基址寄存器(TTBR0或TTBR1)中的一个中,具体取决于虚拟地址的最高n位是否为0(使用TTBR0)(使用TTBR1)。 n的值由转换表基本控制寄存器(TTBCR)定义。
寄存器TTBCR
告诉我们将从TTBR0
或TTBR1
指向的页表中翻译哪些地址。如果TTBCR
包含0xc000000
,那么从0
到0xbfffffff
的任何地址都由TTBR0
指向的页面表翻译,而0xc0000000
到0xffffffff
的任何地址都由TTBR1
指向的页面表翻译。这与用户进程的3GB内存分配相匹配,内核为1GB。
这样就可以实现一种设计,其中操作系统和内存映射I / O位于地址空间的上部,并由TTBR1中的页表管理,用户进程位于内存的下半部分,由TTB0中的页表。在上下文切换时,操作系统必须将TTBR0更改为指向新进程的第一级表。 TTBR1仍将包含操作系统和内存映射I / O的内存映射。
因此,TTBR1
的值永远不会改变,因为你希望内核被永久映射(想想当引发中断时会发生什么)。另一方面,TTBR0
在每个进程切换时都被修改,它包含当前进程的页表。
见http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211k/Bihgfcgf.html
对于ARM5及更低版本,TTB表的大小和对齐方式是固定的(至16k)。每个1级条目代表1MB。表项是32位(16k * 1M /(32位/ 8)= 4GB)。 TTBCR控制TTBR0表格大小。从上面的URL,
选择使用哪个转换表基址寄存器 翻译表基址寄存器选择如下: 如果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,但不使用pid或fcse,因为它支持有限数量的进程。
http://lwn.net/Articles/106177/ - 也在Rutgers页面上引用。
TTBR0保存转换表0的基址,以及它占用的内存信息。
这是从Hyp模式以外的模式进行存储器访问的第1阶段转换的转换表之一
以上是关于arm linux 怎么把内核空间的寄存器映射到用户 空间的主要内容,如果未能解决你的问题,请参考以下文章
Linux 内核 内存管理内存映射原理 ① ( 物理地址空间 | 外围设备寄存器 | 外围设备寄存器的物理地址 映射到 虚拟地址空间 )