文章出自於 trace linux kernel source - ARM - 03 part2
隔了很長一段時間沒update
之前程式碼走到 ./arch/arm/kernel/head.S 的 line 99 附近
99 ldr r13, __switch_data @ address to jump to after
100 @ mmu has been enabled
101 adr lr, __enable_mmu @ return (PIC) address
102 add pc, r10, #PROCINFO_INITFUNC
line 99, 將__switch_data放到r13。(留作之後用)
line 101, 將__enable_mmu的addr放到lr。(留作之後用)
line 102, 將 r10+#PROCINFO_INITFUNC 放到pc,也就是jump過去的意思。r10是proc_info的位址。PROCINFO_INITFUNC則是用之前提過的技巧,指向定義在./arch/arm/mm/proc-xxx.S的資料結構,以arm926為例,最後會指到
463 b __arm926_setup
所以程式碼就跳到了 __arm926_setup。
373 .type __arm926_setup, #function
374 __arm926_setup:
375 mov r0, #0
376 mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4
377 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4
378 #ifdef CONFIG_MMU
379 mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4
380 #endif
388 adr r5, arm926_crval
389 ldmia r5, {r5, r6}
390 mrc p15, 0, r0, c1, c0 @ get control register v4
391 bic r0, r0, r5
392 orr r0, r0, r6
396 mov pc, lr
397 .size __arm926_setup, . - __arm926_setup
這邊的程式碼就跟CPU有很大的相依性,
line 375~380, 主要就是invalidate CPU的I&D cache和清空write buffer。
line 388~392, 把cp15的設定讀出來,並且將一些預設狀態做好運算放到r0。(預設值從arm926_crval這邊可以取得。)
line 396, 直接把pc跳到lr,因為我們之前已經將lr = enable_mmu,所以直接跳過去。
155 __enable_mmu:
170 mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
171 domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
172 domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
173 domain_val(DOMAIN_IO, DOMAIN_CLIENT))
174 mcr p15, 0, r5, c3, c0, 0 @ load domain access register
175 mcr p15, 0, r4, c2, c0, 0 @ load page table pointer
176 b __turn_mmu_on
177 ENDPROC(__enable_mmu)
line 170~174,設置好domain access。(domain access可以先當成設置access的權限,或許以後可以寫詳細的文章說明)
line 175~176,將create好的page table開頭丟給mmu。跳到turn_mmu_on
191 __turn_mmu_on:
192 mov r0, r0
193 mcr p15, 0, r0, c1, c0, 0 @ write control reg
194 mrc p15, 0, r3, c0, c0, 0 @ read id reg
195 mov r3, r3
196 mov r3, r3
197 mov pc, r13
198 ENDPROC(__turn_mmu_on)
顧名思義就是把mmu打開,將我們準備好的r0設定交給mmu,並讀取id到r3,接著pc跳到r13,r13剛剛在head.S已經先擺好__switch_data。所以會跳到head-common.S。
18 __switch_data:
19 .long __mmap_switched
20 .long __data_loc @ r4
21 .long _data @ r5
22 .long __bss_start @ r6
23 .long _end @ r7
24 .long processor_id @ r4
25 .long __machine_arch_type @ r5
26 .long __atags_pointer @ r6
27 .long cr_alignment @ r7
28 .long init_thread_union + THREAD_START_SP @ sp
29
switch_data的第一行就是 __mmap_switched,所以我們直接看line 39。
39 __mmap_switched:
40 adr r3, __switch_data + 4
41
42 ldmia r3!, {r4, r5, r6, r7}
43 cmp r4, r5 @ Copy data segment if needed
44 1: cmpne r5, r6
45 ldrne fp, [r4], #4
46 strne fp, [r5], #4
47 bne 1b
48
49 mov fp, #0 @ Clear BSS (and zero fp)
50 1: cmp r6, r7
51 strcc fp, [r6],#4
52 bcc 1b
53
54 ldmia r3, {r4, r5, r6, r7, sp}
55 str r9, [r4] @ Save processor ID
56 str r1, [r5] @ Save machine type
57 str r2, [r6] @ Save atags pointer
58 bic r4, r0, #CR_A @ Clear 'A' bit
59 stmia r7, {r0, r4} @ Save control register values
60 b start_kernel
61 ENDPROC(__mmap_switched)
switch_data的第一行就是 __mmap_switched,所以我們直接看line 39。
line 39,將__data_loc的addr放到r3
line 42,從r3的位址,連續讀取四筆資料到r4, r5, r6, r7
line 43~47,看看data segment是不是需要搬動。
line 49~52, clear BSS。
由於linux kernel在進入start_kernel前有一些前提必須要滿足:
r0 = cp#15 control register
r1 = machine ID
r2 = atags pointer
r9 = processor ID
所以line 54~59就是在做這些準備。
最後呢? 我們就跳到start_kernel了。(而且還是用b start_kernel,表示我們不會再回來了)
看一下start_kernel()在./init/main.c,終於跳出architecture specific的目錄,表示
我們真正的開始linux kernel的初始化。
像是 shedule init, console init, memory init, irq init等等都在start_kernel裡頭。
到這邊之後,應該就可以深入linux kernel中,各項比較跟hardware不那麼相關的軟體部分。
之前程式碼走到 ./arch/arm/kernel/head.S 的 line 99 附近
99 ldr r13, __switch_data @ address to jump to after
100 @ mmu has been enabled
101 adr lr, __enable_mmu @ return (PIC) address
102 add pc, r10, #PROCINFO_INITFUNC
line 99, 將__switch_data放到r13。(留作之後用)
line 101, 將__enable_mmu的addr放到lr。(留作之後用)
line 102, 將 r10+#PROCINFO_INITFUNC 放到pc,也就是jump過去的意思。r10是proc_info的位址。PROCINFO_INITFUNC則是用之前提過的技巧,指向定義在./arch/arm/mm/proc-xxx.S的資料結構,以arm926為例,最後會指到
463 b __arm926_setup
所以程式碼就跳到了 __arm926_setup。
373 .type __arm926_setup, #function
374 __arm926_setup:
375 mov r0, #0
376 mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4
377 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4
378 #ifdef CONFIG_MMU
379 mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4
380 #endif
388 adr r5, arm926_crval
389 ldmia r5, {r5, r6}
390 mrc p15, 0, r0, c1, c0 @ get control register v4
391 bic r0, r0, r5
392 orr r0, r0, r6
396 mov pc, lr
397 .size __arm926_setup, . - __arm926_setup
這邊的程式碼就跟CPU有很大的相依性,
line 375~380, 主要就是invalidate CPU的I&D cache和清空write buffer。
line 388~392, 把cp15的設定讀出來,並且將一些預設狀態做好運算放到r0。(預設值從arm926_crval這邊可以取得。)
line 396, 直接把pc跳到lr,因為我們之前已經將lr = enable_mmu,所以直接跳過去。
155 __enable_mmu:
170 mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
171 domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
172 domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
173 domain_val(DOMAIN_IO, DOMAIN_CLIENT))
174 mcr p15, 0, r5, c3, c0, 0 @ load domain access register
175 mcr p15, 0, r4, c2, c0, 0 @ load page table pointer
176 b __turn_mmu_on
177 ENDPROC(__enable_mmu)
line 170~174,設置好domain access。(domain access可以先當成設置access的權限,或許以後可以寫詳細的文章說明)
line 175~176,將create好的page table開頭丟給mmu。跳到turn_mmu_on
191 __turn_mmu_on:
192 mov r0, r0
193 mcr p15, 0, r0, c1, c0, 0 @ write control reg
194 mrc p15, 0, r3, c0, c0, 0 @ read id reg
195 mov r3, r3
196 mov r3, r3
197 mov pc, r13
198 ENDPROC(__turn_mmu_on)
顧名思義就是把mmu打開,將我們準備好的r0設定交給mmu,並讀取id到r3,接著pc跳到r13,r13剛剛在head.S已經先擺好__switch_data。所以會跳到head-common.S。
18 __switch_data:
19 .long __mmap_switched
20 .long __data_loc @ r4
21 .long _data @ r5
22 .long __bss_start @ r6
23 .long _end @ r7
24 .long processor_id @ r4
25 .long __machine_arch_type @ r5
26 .long __atags_pointer @ r6
27 .long cr_alignment @ r7
28 .long init_thread_union + THREAD_START_SP @ sp
29
switch_data的第一行就是 __mmap_switched,所以我們直接看line 39。
39 __mmap_switched:
40 adr r3, __switch_data + 4
41
42 ldmia r3!, {r4, r5, r6, r7}
43 cmp r4, r5 @ Copy data segment if needed
44 1: cmpne r5, r6
45 ldrne fp, [r4], #4
46 strne fp, [r5], #4
47 bne 1b
48
49 mov fp, #0 @ Clear BSS (and zero fp)
50 1: cmp r6, r7
51 strcc fp, [r6],#4
52 bcc 1b
53
54 ldmia r3, {r4, r5, r6, r7, sp}
55 str r9, [r4] @ Save processor ID
56 str r1, [r5] @ Save machine type
57 str r2, [r6] @ Save atags pointer
58 bic r4, r0, #CR_A @ Clear 'A' bit
59 stmia r7, {r0, r4} @ Save control register values
60 b start_kernel
61 ENDPROC(__mmap_switched)
switch_data的第一行就是 __mmap_switched,所以我們直接看line 39。
line 39,將__data_loc的addr放到r3
line 42,從r3的位址,連續讀取四筆資料到r4, r5, r6, r7
line 43~47,看看data segment是不是需要搬動。
line 49~52, clear BSS。
由於linux kernel在進入start_kernel前有一些前提必須要滿足:
r0 = cp#15 control register
r1 = machine ID
r2 = atags pointer
r9 = processor ID
所以line 54~59就是在做這些準備。
最後呢? 我們就跳到start_kernel了。(而且還是用b start_kernel,表示我們不會再回來了)
看一下start_kernel()在./init/main.c,終於跳出architecture specific的目錄,表示
我們真正的開始linux kernel的初始化。
像是 shedule init, console init, memory init, irq init等等都在start_kernel裡頭。
到這邊之後,應該就可以深入linux kernel中,各項比較跟hardware不那麼相關的軟體部分。
沒有留言:
張貼留言