从内核模块中查找异常向量表的物理地址
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_phys
和 phys_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表是virt到phys的列表;所以两个条目指向同一个 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 系列。
【讨论】:
以上是关于从内核模块中查找异常向量表的物理地址的主要内容,如果未能解决你的问题,请参考以下文章
Linux进程概念——下验证进程地址空间的基本排布 | 理解进程地址空间 | 进程地址空间如何映射至物理内存(页表的引出) | 为什么要存在进程地址空间 | Linux2.6内核进程调度队列
Linux进程概念——下验证进程地址空间的基本排布 | 理解进程地址空间 | 进程地址空间如何映射至物理内存(页表的引出) | 为什么要存在进程地址空间 | Linux2.6内核进程调度队列
Linux进程概念——下验证进程地址空间的基本排布 | 理解进程地址空间 | 进程地址空间如何映射至物理内存(页表的引出) | 为什么要存在进程地址空间 | Linux2.6内核进程调度队列