OAL的启动代码和EBOOT的启动代码经常复用,但为了代码的简洁,最好还是分开实现,而且在EBOOT中如果已经初始化了相关硬件,那么OAL的启动代码就可以省去那部分工作,可以很简练,如上面的代码所示。
可以看出,OAL的启动代码又调用了函数KernelStart(),而这个函数是在文件C:\WINCE600\PRIVATE\WINCEOS\COREOS\NK\LDR\ARM\armstart.s中实现的,代码如下:
Code
LEAF_ENTRY KernelStart
mov r11, r0 ; (r11) = &OEMAddressTable (save pointer)
; figure out the virtual address of OEMAddressTable
mov r1, r11 ; (r1) = &OEMAddressTable (2nd argument to VaFromPa)
bl VaFromPa
mov r6, r0 ; (r6) = VA of OEMAddressTable
; convert base of PTs to Physical address
ldr r4, =PTs ; (r4) = virtual address of FirstPT
mov r0, r4 ; (r0) = virtual address of FirstPT
mov r1, r11 ; (r1) = &OEMAddressTable (2nd argument to PaFromVa)
bl PaFromVa
mov r10, r0 ; (r10) = ptr to FirstPT (physical)
; Zero out page tables & kernel data page
mov r0, #0 ; (r0-r3) = 0's to store
mov r1, #0
mov r2, #0
mov r3, #0
mov r4, r10 ; (r4) = first address to clear
add r5, r10, #KDEnd-PTs ; (r5) = last address + 1
18 stmia r4!, {r0-r3}
stmia r4!, {r0-r3}
cmp r4, r5
blo %B18
; read the architecture information
bl GetCpuId
mov r5, r0 LSR #16 ; r5 >>= 16
and r5, r5, #0x0000000f ; r5 &= 0x0000000f == architecture id
; Setup 2nd level page table to map the high memory area which contains the
; first level page table, 2nd level page tables, kernel data page, etc.
; (r5) = architecture id
add r4, r10, #HighPT-PTs ; (r4) = ptr to high page table
cmp r5, #ARMv6 ; v6 or later?
; ARMV6_MMU
orrge r0, r10, #PTL2_KRW + PTL2_SMALL_PAGE + ARMV6_MMU_PTL2_SMALL_XN
; (r0) = PTE for 4K, kr/w u-/- page, uncached unbuffered, nonexecutable
; PRE ARMV6_MMU
orrlt r0, r10, #PTL2_KRW + (PTL2_KRW << 2) + (PTL2_KRW << 4) + (PTL2_KRW << 6)
; Need to replicate AP bits into all 4 fields
orrlt r0, r0, #PTL2_SMALL_PAGE + PREARMV6_MMU_PTL2_SMALL_XN
; (r0) = PTE for 4K, kr/w u-/- page, uncached unbuffered, nonexecutable
str r0, [r4, #0xD0*4] ; store the entry into 4 slots to map 16K of primary page table
add r0, r0, #0x1000 ; step on the physical address
str r0, [r4, #0xD1*4]
add r0, r0, #0x1000 ; step on the physical address
str r0, [r4, #0xD2*4]
add r0, r0, #0x1000 ; step on the physical address
str r0, [r4, #0xD3*4]
add r8, r10, #ExceptionVectors-PTs ; (r8) = ptr to vector page
orr r0, r8, #PTL2_SMALL_PAGE ; construct the PTE (C=B=0)
;; The exception stacks and the vectors are mapped as a single kr/w page.
;; Any alternative will use more physical memory.
;; Multiple mappings don't provide any real protection: if the vectors were in a r/o page,
;; they could still be corrupted via the kr/w setting required for the stacks.
cmp r5, #ARMv6 ; v6 or later?
; ARMV6_MMU
orrge r0, r0, #PTL2_KRW
; PRE ARMV6_MMU
orrlt r0, r0, #PTL2_KRW + (PTL2_KRW << 2) + (PTL2_KRW << 4) + (PTL2_KRW << 6)
; Need to replicate AP bits into all 4 fields for pre-V6 MMU
str r0, [r4, #0xF0*4] ; store entry for exception stacks and vectors
; other 3 entries now unused
add r9, r10, #KPage-PTs ; (r9) = ptr to kdata page
orr r0, r9, #PTL2_SMALL_PAGE ; (r0)=PTE for 4K (C=B=0)
; ARMV6_MMU (condition codes still set)
orrge r0, r0, #PTL2_KRW_URO ; No subpage access control, so we must set this all to kr/w+ur/o
; PRE ARMV6_MMU
orrlt r0, r0, #(PTL2_KRW << 0) + (PTL2_KRW << 2) + (PTL2_KRW_URO << 4)
; (r0) = set perms kr/w kr/w kr/w+ur/o r/o
str r0, [r4, #0xFC*4] ; store entry for kernel data page
orr r0, r4, #PTL1_2Y_TABLE ; (r0) = 1st level PTE for high memory section
add r1, r10, #0x4000
str r0, [r1, #-4] ; store PTE in last slot of 1st level table
; Fill in first level page table entries to create "statically mapped" regions
; from the contents of the OEMAddressTable array.
;
; (r5) = architecture id
; (r9) = ptr to KData page
; (r10) = ptr to 1st level page table
; (r11) = ptr to OEMAddressTable array
add r10, r10, #0x2000 ; (r10) = ptr to 1st PTE for "unmapped space"
mov r0, #PTL1_SECTION
orr r0, r0, #PTL1_KRW ; (r0)=PTE for 0: 1MB (C=B=0, kernel r/w)
20 mov r1, r11 ; (r1) = ptr to OEMAddressTable array (physical)
25 ldr r2, [r1], #4 ; (r2) = virtual address to map Bank at
ldr r3, [r1], #4 ; (r3) = physical address to map from
ldr r4, [r1], #4 ; (r4) = num MB to map
cmp r4, #0 ; End of table?
beq %F29
ldr r12, =0x1FF00000
and r2, r2, r12 ; VA needs 512MB, 1MB aligned.
ldr r12, =0xFFF00000
and r3, r3, r12 ; PA needs 4GB, 1MB aligned.
add r2, r10, r2, LSR #18
add r0, r0, r3 ; (r0) = PTE for next physical page
28 str r0, [r2], #4
add r0, r0, #0x00100000 ; (r0) = PTE for next physical page
sub r4, r4, #1 ; Decrement number of MB left
cmp r4, #0
bne %B28 ; Map next MB
bic r0, r0, #0xF0000000 ; Clear Section Base Address Field
bic r0, r0, #0x0FF00000 ; Clear Section Base Address Field
b %B25 ; Get next element
29
sub r10, r10, #0x2000 ; (r10) = restore address of 1st level page table
; The minimal page mappings are setup. Initialize the MMU and turn it on.
; there are some CPUs with pipeline issues that requires identity mapping before turning on MMU.
; We'll create an identity mapping for the address we'll jump to when turning on MMU on and remove
; the mapping after we turn on MMU and running on Virtual address.
ldr r12, =0xFFF00000 ; (r12) = mask for section bits
and r1, pc, r12 ; physical address of where we are
; NOTE: we assume that the KernelStart function never spam across 1M boundary.
orr r0, r1, #PTL1_SECTION
orr r0, r0, #PTL1_KRW ; (r0) = PTE for 1M for current physical address, C=B=0, kernel r/w
add r7, r10, r1, LSR #18 ; (r7) = 1st level PT entry for the identity map
ldr r8, [r7] ; (r8) = saved content of the 1st-level PT
str r0, [r7] ; create the identity map
mov r1, #1
mtc15 r1, c3 ; Setup access to domain 0 and clear other
mtc15 r10, c2 ; setup translation base (physical of 1st level PT)
mov r0, #0
mcr p15, 0, r0, c8, c7, 0 ; Flush the I&D TLBs
mfc15 r1, c1
orr r1, r1, #0x007F ; changed to read-mod-write for ARM920 Enable: MMU, Align, DCache, WriteBuffer
cmp r5, #ARMv6 ; r5 still set
; ARMV6_MMU
orrge r1, r1, #0x3000 ; vector adjust, ICache
orrge r1, r1, #1<<23 ; V6-format page tables
orrge r1, r1, #ARMV6_U_BIT ; V6-set U bit, let A bit control unalignment support
; PRE ARMV6_MMU
orrlt r1, r1, #0x3200 ; vector adjust, ICache, ROM protection
ldr r0, VirtualStart
cmp r0, #0 ; make sure no stall on "mov pc,r0" below
mtc15 r1, c1 ; enable the MMU & Caches
mov pc, r0 ; & jump to new virtual address
nop
; MMU & caches now enabled.
;
; (r10) = physcial address of 1st level page table
; (r7) = entry in 1st level PT for identity map
; (r8) = saved 1st level PT save at (r7)
VStart ldr r2, =FirstPT ; (r2) = VA of 1st level PT
sub r7, r7, r10 ; (r7) = offset into 1st-level PT
str r8, [r2, r7] ; restore the temporary identity map
mcr p15, 0, r0, c8, c7, 0 ; Flush the I&D TLBs
;
; setup stack for each modes: current mode = supervisor mode
;
ldr sp, =KStack
add r4, sp, #KData-KStack ; (r4) = ptr to KDataStruct
; setup ABORT stack
mov r1, #ABORT_MODE:OR:0xC0
msr cpsr_c, r1 ; switch to Abort Mode w/IRQs disabled
add sp, r4, #AbortStack-KData
; setup IRQ stack
mov r2, #IRQ_MODE:OR:0xC0
msr cpsr_c, r2 ; switch to IRQ Mode w/IRQs disabled
add sp, r4, #IntStack-KData
; setup FIQ stack
mov r3, #FIQ_MODE:OR:0xC0
msr cpsr_c, r3 ; switch to FIQ Mode w/IRQs disabled
add sp, r4, #FIQStack-KData
; setup UNDEF stack
mov r3, #UNDEF_MODE:OR:0xC0
msr cpsr_c, r3 ; switch to Undefined Mode w/IRQs disabled
mov sp, r4 ; (sp_undef) = &KData
; switch back to Supervisor mode
mov r0, #SVC_MODE:OR:0xC0
msr cpsr_c, r0 ; switch to Supervisor Mode w/IRQs disabled
ldr sp, =KStack
; continue initialization in C
add r0, sp, #KData-KStack ; (r0) = ptr to KDataStruct
str r6, [r0, #pAddrMap] ; store VA of OEMAddressTable in KData
bl ARMInit ; call C function to perform the rest of initializations
; upon return, (r0) = entry point of kernel.dll
mov r12, r0
ldr r0, =KData
mov pc, r12 ; jump to entry of kernel.dll
从上面的代码可以看出,KernelStart()通过OEMAddressTable初始化了MMU,然后通过调用函数ARMInit()获得kernel.dll的入口点,最后跳转到kernel.dll的入口点处。
为了找到Kernel.dll的入口点,用IDA反汇编kernel.dll文件,可以看到,Kernel.dll的入口点为NKStartup。
NKStartup()的实现在文件C:\WINCE600\PRIVATE\WINCEOS\COREOS\NK\KERNEL\ARM\ mdarm.c中,代码如下: