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

4

我有一台安卓设备 - 三星Galaxy S2,内核版本为2.6.35.14(ARM Cortex A9)

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

我还知道大多数内核虚拟地址(到物理地址的转换)是通过减去0x8000000的值完成的。

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

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

是否有人知道为什么会发生这种情况?异常向量表的地址是否驻留在特殊的物理地址中?内核是否以某种特殊的方式转换其地址?

谢谢!

2个回答

6
异常向量表在Linux中从2.6.35到最新的主线版保持相对稳定。它在引导阶段非常早地被分配,并涉及memblock引导分配器。相关文件包括内核目录中的entry-armv.Straps.cvmlinux.lds.S(链接脚本),以及mm(或内存管理ARM目录)中的init.cmmu.c。此外,向量表必须始终映射,可被用户进程读取。这被内核用户助手使用;辅助例程也映射到此页面。
vmlinux.lds.S中,

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

__vectors_start表示向量页中的代码,并在entry-armv.S中找到。然而,该函数在traps.c中被重新定位。如果配置了高向量,则引导分配器为虚拟0xffff000地址保留一页。在您的2.6.35内核中,在init.c中使用了memblock分配器。在这里,mmu.cdevicemaps_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 的虚拟别名,它映射到相同的物理页面。这种 别名 的原因应该很明显。大多数 ARM CPU 需要将向量放在 0 或 0xffff0000,尽管较新的 ARM 可以省略此步骤。 - artless noise
谢谢您的快速回答!那么让我们看看我是否理解正确。memblock_alloc()函数返回一个指向向量表(变量 - vector)的虚拟地址。此外,内核必须将地址0xffff0000映射到之前分配的物理地址。因此,在内核中有两个虚拟地址被映射到相同的物理地址,或者在0xffff0000映射之后,第一个地址不再相关? - user1010822
memblock_alloc() 分配一个正常的物理/虚拟对。然后在 devicemaps_init() 中,创建第二个别名指向 0xffff0000。第一个虚拟地址是相关的并保持活动状态;它是 devicemaps_init()vector 指针的值。它仍然存在于 MMU 表中;MMU 表是一个虚拟物理的列表;因此两个条目指向相同的物理页面。正如您所观察到的,0xffff0000 不遵循正常规则。 - artless noise
这个别名有一个重要的警告。如果你想修改其中一个(而不是0xffff0000),你需要清除缓存写入缓冲区,否则旧值可能会在一段时间内保留(在缓存中的0xffff0000值)。缓存会制作同样数据的第二份副本,它不会知道别名。 - artless noise

2
也可以直接使用MMU(ATS1Cxx和PAR寄存器)来执行虚拟地址到物理地址的转换。请参见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系列。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接