从内核模块中查找异常向量表的物理地址

Posted

技术标签:

【中文标题】从内核模块中查找异常向量表的物理地址【英文标题】:Find the physical address of exception vector table from kernel module 【发布时间】:2013-10-17 00:42:50 【问题描述】:

我有一个 android 设备 - 内核版本为 2.6.35.14 (arm cortex a9) 的三星 Galaxy s2

我试图找到异常向量表的物理地址。我知道它位于 0xffff0000 虚拟地址。 (我可以通过内核模块打印它的值)

我也知道大部分内核虚拟地址(到物理地址)的转换是由值 0x8000000 的 substation 完成的。

我有一个可以直接从设备内存读取数据的设备,我想获取异常向量表。

当我构建内核模块并尝试使用宏 virt_to_phys(0xffff0000) 时,我得到了一些地址,但表不存在。我通过这种方式成功找到了系统调用表,但是这里的宏给了我错误的地址。

有人知道为什么会这样吗?异常向量表的地址是否位于特殊的物理地址中?内核是否以某种特殊方式转换其地址?

谢谢!!

【问题讨论】:

【参考方案1】:

异常向量表在 Linux 中一直保持相当稳定,从 2.6.35 到最新的主线。它在引导阶段很早就被分配并涉及 memblock 引导分配器。涉及的文件是内核目录中的entry-armv.S、traps.c 和vmlinux.lds.S(链接描述文件),mm 中的init.c 和mmu.c(或内存管理 ARM 目录)。此外,向量表必须一直被映射并且它可以被用户进程读取。这是kernel user helpers使用的;辅助例程也映射到此页面。

解释

来自vmlinux.lds.S

向量和存根是可重定位代码,唯一重要的是它们的相对偏移量

__vectors_start 代表向量页面中的代码,在entry-armv.S中找到。但是,该函数被重新定位在 traps.c 中。 引导分配器为虚拟 0xffff000 地址保留一个页面(如果配置了高向量)。在您的 2.6.35 内核中,memblock 分配器用于init.c。这里mmu.c'sdevicemaps_init()通过调用early_alloc()分配一个页面。此页面不遵循正常的内核地址空间规则,virt_to_phys 可能无法使用,因为 虚拟地址 是强制的。

但是,内核地址确实存在原始memblock_alloc() 返回地址。这是devicemaps_init()中的指针vector;此地址适用于 virt_to_physphys_to_virt

entry-armv.S__vectors_start等的物理地址很容易找到并计算出物理地址;但是,它在 init 阶段结束时被丢弃,我认为您对此不感兴趣。

答案

您可以调用memblock_dump_all(void) 并查看dmesg 并使用这些指针尝试定位vector 页面。它将是 4k 大小。您可以更改 devicemaps_init() 以导出 vector 值。未修改内核的唯一方法是遍历 ARM mmu 表;那是另一个故事。

【讨论】:

也就是说devicemaps_init中的vector指针是0xffff0000的虚拟别名,映射到同一个物理页面。 alias 的原因应该很明显。大多数 ARM CPU 需要 0 或 0xffff0000 的向量,尽管较新的 ARM 可以放弃这个。 感谢您的快速答复!所以让我看看我是否理解正确。 memblock_alloc() 返回一个指向向量表的虚拟地址(变量-向量)。此外,内核必须将地址 0xffff0000 映射到之前分配的物理地址。所以内核中有 2 个虚拟地址映射到同一个物理地址,还是在 0xffff0000 映射后第一个不相关? memblock_alloc() 分配一个 normal 物理/虚拟对。然后在devicemaps_init() 中,为 0xffff0000 创建了第二个别名。第一个虚拟地址是相关的并保持活动状态;它是devicemaps_init()vector 指针的值。它保留在 MMU 表中; MMU表是virtphys的列表;所以两个条目指向同一个 phys 页面。 0xffff0000 不遵循您观察到的正常规则。 具有此别名的重要警告。如果您打算修改其中一个(不是 0xffff0000),则需要刷新缓存写入缓冲区,否则旧值可能会保留一段时间(在缓存中的 0xffff0000 值中)。缓存会复制相同数据的第二个副本,它不会知道别名。【参考方案2】:

也可以直接使用 MMU(寄存器 ATS1Cxx 和 PAR)来执行 V=>P 转换。有关血腥细节,请参阅ARM ARM 的 B4.2.4 部分。

如果您以这种方式使用 MMU 寄存器,您可能需要防止与寄存器的其他用途发生竞争。

这可以通过以下代码从任何内核驱动程序访问,

 unsigned int pa;
 asm("\t mcr p15, 0, %0, c7, c8, 2\n"
     "\t isb\n"
     "\t mrc p15, 0, %0, c7, c4, 0\n" : "=r" (pa) : "0" (0xffff0000));
 printk("Vector is %x\n", pa & 0xfffff000);

常数是正确的。

0xffff0000 是高位向量虚拟地址。 0xfffff000 是 4k 页面的掩码。

这仅适用于以后的系列 ARM 处理器,例如 Cortex 系列。

【讨论】:

以上是关于从内核模块中查找异常向量表的物理地址的主要内容,如果未能解决你的问题,请参考以下文章

虚拟地址转换为物理地址

在向量 GENy 中配置闪存块表

内核物理地址和虚拟地址之间的静态映射过程

Linux进程概念——下验证进程地址空间的基本排布 | 理解进程地址空间 | 进程地址空间如何映射至物理内存(页表的引出) | 为什么要存在进程地址空间 | Linux2.6内核进程调度队列

Linux进程概念——下验证进程地址空间的基本排布 | 理解进程地址空间 | 进程地址空间如何映射至物理内存(页表的引出) | 为什么要存在进程地址空间 | Linux2.6内核进程调度队列

Linux进程概念——下验证进程地址空间的基本排布 | 理解进程地址空间 | 进程地址空间如何映射至物理内存(页表的引出) | 为什么要存在进程地址空间 | Linux2.6内核进程调度队列