3. 从KernelStart开始2
KernelStart()函数代码如下。
LEAF_ENTRY KernelStart
;我们现在跑在物理地址下;
;如前所述,r0在之前已经保存了MemoryMap的物理地址。
mov r11, r0 ; (r11) = &MemoryMap (save pointer)
; figure out the virtual address of OEMAddressTable
;将MemoryMap的物理地址转换成虚拟地址,保存在r6中,以后会经常用的。
mov r1, r11 ; (r1) = &MemoryMap (2nd argument to VaFromPa)
bl VaFromPa
mov r6, r0 ; (r6) = VA of MemoryMap
; convert base of PTs to Physical address
;注意这里,=PTs居然是一个虚拟地址。现在不是跑在物理地址下吗?为什么=PTs居然返回虚拟地址?
;答案在在config.bib里。这个文件会有一行说明eboot(也有可能是msipl)加载内核nk的地址,注意这个地址是虚拟地址,例如
;NK 80300000 00200000 RAMIMAGE
;nk.nb0的开始部分是以0x80000000为基址链接过的内核文件nk.exe。也就是说此时所有的lable返回的都是虚拟地址。
;换句话说,现在是在物理地址下跑虚拟地址的映像,这时只要访问的是相对地址而不是绝对地址,是不会跑飞的。
;下面代码将FirstPT的物理地址保存在r10中,以后会用到。
ldr r4, =PTs ; (r4) = virtual address of FirstPT
mov r0, r4 ; (r0) = virtual address of FirstPT
mov r1, r11 ; (r1) = &MemoryMap (2nd argument to PaFromVa)
bl PaFromVa
mov r10, r0 ; (r10) = ptr to FirstPT (physical)
这里有必要说一下ce内核的高地址虚拟空间(0x0xFFFD0000到0x0xFFFD0000)的使用。在armtrap.s文件的开头,我们会看到下面几行。
; High memory layout:
; FFFD0000 - first level page table (uncached)
; FFFD4000 - not used
; FFFE0000 - disabled for protection
; FFFF0000 - exception vectors
; FFFF03E0 - exception vector jump table
; FFFF0400 - not used (r/o)
; FFFF1000 - disabled for protection
; FFFF2000 - r/o (physical overlaps with vectors)
; FFFF2400 - Interrupt stack (1k)
; FFFF2800 - r/o (physical overlaps with Abort stack)
; FFFF3000 - disabled for protection
; FFFF4000 - r/o (physical memory overlaps with vectors & intr. stack & FIQ stack)
; FFFF4900 - Abort stack (2k - 256 bytes)
; FFFF5000 - disabled for protection
; FFFF6000 - r/o (physical memory overlaps with vectors & intr. stack)
; FFFF6800 - FIQ stack (256 bytes)
; FFFF6900 - r/o (physical memory overlaps with Abort stack)
; FFFF7000 - disabled
; FFFFC000 - kernel stack
; FFFFC800 - KDataStruct
; FFFFCC00 - r/o for protection (2nd level page table for 0xFFF00000)
^ 0xFFFD0000
FirstPT # 0x4000
# 0x4000
# 0x8000
# 0x10000 ; not mapped
ExVector # 0x1000
# 0x1400 ; not mapped
# 0x0400 ; 1K interrupt stack
IntStack # 0x2000 ; not mapped (ffff2800)
# 0x0100 ; not mapped (FIQ stack) (ffff4800)
# 0x0700 ; 2K-256 abort stack (ffff4900)
AbortStack # 0x1800 ; not mapped (ffff5000)
# 0x0100 ; not mapped (FIQ stack) (ffff6800)
FIQStack # 0xC000-0x6900 ; not mapped (ffff6900)
KDBase # 0x07E0 ; 2K-32 kernel stack
KStack # 0x0020 ; temporary register save area
KData # 0x400 ; kernel data area
;-----------------------------------------------------------------------
; .KDATA area is used to reserve physical memory for the above structures.
AREA |.KDATA|,DATA,NOINIT
EXPORT ExceptionVectors
KDataArea
PTs % 0x4000 ; space for first-level page table
ExceptionVectors
% 0x0400 ; space for exception vectors
% 0x0400 ; space for interrupt stack
% 0x0100 ; space for FIQ stack
% 0x0700 ; space for Abort stack
KPage % 0x0c00 ; space for kernel stack & KDataStruct
HighPT % 0x0400 ; space for 2nd level page table to map 0xFFF00000
KDEnd % 0
这段代码分为三个部分。第一部分是注释,详细解释了第二部分的内容。第二部分以0xFFFD0000为起始地址定义了一个数据结构(关于^和%等的含义参考文档1),可以通过将这个数据结构的每一项和注释对应起来了解其字面意思,具体含义以后会解释。第三部分分配了一块物理内存,来实现对上述数据结构的物理存储。在这里有两点要注意:第一是,该物理内存并没有和第二部分的数据结构对应起来。这是因为第二部分的数据结构中间有很多没有使用的"空洞",如果物理内存也要分配空间给这些"空洞"那就太浪费了。所以只需要给有意义的部分分配物理内存,在进行虚拟地址到物理地址映射的时候给虚拟地址最大小适合的page,通过在代码访问超出这个page大小的时候发生异常,同时在访问这些虚拟地址的时候谨慎的检查,来确保不会访问到"空洞"的虚拟地址;第二是从现在开始要注意源代码给出的关于PTs和FirstPT的使用和注释,之间有微妙的区别,这些都会在下面结合代码加以解释。
http://hi.baidu.com/garnetttt/blog/item/819ca7862d58ee3d67096ec3.html