地址的一些宏定义
CPU不可能一直执行位置无关指令,所以必须将 kernel 本身的虚拟地址(运行地址)映射到它实际所在的物理地址,这个通过为 MMU 创建页表来实现,一旦页表创建完成,并打开了MMU,CPU发出的虚拟地址,可以由 MMU 转换成实际的物理地址,这样CPU执行kernel代码将不再受限。
KIMAGE_VADDR
定义:arch/arm64/include/asm/memory.h
#define KIMAGE_VADDR (MODULES_END)
default 39 if ARM64_VA_BITS_39 为例:
Virtual kernel memory layout:
modules : 0xffffff8000000000 - 0xffffff8008000000 ( 128 MB)
vmalloc : 0xffffff8008000000 - 0xffffffbdbfff0000 ( 246 GB)
.init : 0xffffff8008c00000 - 0xffffff8008e00000 ( 2048 KB)
.text : 0xffffff8008080000 - 0xffffff8008800000 ( 7680 KB)
.rodata : 0xffffff8008800000 - 0xffffff8008c00000 ( 4096 KB)
.data : 0xffffff8008e00000 - 0xffffff8008e84a00 ( 531 KB)
fixed : 0xffffffbffe7fd000 - 0xffffffbffec00000 ( 4108 KB)
PCI I/O : 0xffffffbffee00000 - 0xffffffbfffe00000 ( 16 MB)
memory : 0xffffffc010000000 - 0xffffffc050000000 ( 1024 MB)
按 48 bits 理解:
对于ARM64来说,虚拟地址的寻址能力一般是48 bits,当然也支持其他的寻址能力,我们这里按48 bits来作说明。按48 bits的寻址能力,分配给内核的起始虚拟地址是0xffff000000000000
,前面会留一部分另做他用,如给KASAN,MODULE
模块使用,后面才分给kernel image
。我们这里假设不支持KASAN
,则给MODULE
预留128M的空间,128M以后给kernel image
使用,也就是说kernel image
的起始地址是0xffff000008000000
。
KERNEL_START
定义:arch/arm64/include/asm/memory.h
#define KERNEL_START _text
_text定义在链接脚本arch/arm64/kernel/vmlinux.lds.S中,是代码段的起始地址,它的绝对地址是0xffff000008080000
。链接脚本定义了kernel image的layout,如果当前PC指针存的是物理地址,对_text相对寻址,可以得到_text的物理地址,也就是kernel image代码段的起始物理地址。
KERNEL_END
定义:arch/arm64/include/asm/memory.h
#define KERNEL_END _end
_end定义在链接脚本中arch/arm64/kernel/vmlinux.lds.S中,是kernel image的结束地址,kernel image出来包括代码段之外,还包括其他段,如BSS段,数据段等等,而_end是整个kernel image的结束地址。
__PHYS_OFFSET
定义:arch/arm64/kernel/head.S
#define __PHYS_OFFSET (KERNEL_START - TEXT_OFFSET)
这个用于计算kernel image的起始地址,起始地址加上TEXT_OFFSET就是代码段的物理地址。
这个地方和 PHYS_OFFSET 区别开来
extern s64 memstart_addr;
/* PHYS_OFFSET - the physical address of the start of memory. */
#define PHYS_OFFSET ({ VM_BUG_ON(memstart_addr & 1); memstart_addr; })
创建idmap页表映射
idmap是kernel image中的一个段,其位于 arch/arm64/kernel/vmlinux.lds.S中,定义如下,即定义了一个以__idmap_text_start开始,__idmap_text_end结束的段,该段会被放在vmlinux的代码段中。
这个段其虚拟地址和物理地址的值相等,其映射关系如下图所示。从图中我们可以看到idmap映射的虚拟地址等于物理地址,因为arm64上物理地址是小于等于48位的,故其虚拟地址就位于0x0000 0000 0000 0000 到0xffff 0000 0000 0000之间,其pgd基地址也相应会被放到ttbr0_el1寄存器中。同时,我们看到它做为kernel image的一部分还会被映射到0xffff 0000 0000 0000和0xffff ffff ffff ffff之间,这个映射关系由后面的swapper映射实现,相应地其pgd的基地址会被放到ttbr1_el1寄存器中。
之所以需要idmap映射时因为现代处理器存在流水线,分支预测等功能,在MMU开启时,打开MMU指令执行时,其后的指令可能已经取指完成,且其地址还是物理地址。而MMU使能完成后,实际上系统已经运行于虚拟地址模式下,若不采取相应措施,此时这些已经取指完成的指令可能会执行错误。故内核采用了将idmap映射的物理地址和虚拟地址设为相等,从而规避了以上问题。