Chip123 科技應用創新平台

標題: trace linux kernel source - ARM - 02 [打印本頁]

作者: gogojesse    時間: 2008-8-8 04:01 PM
標題: trace linux kernel source - ARM - 02
開始跳進去head.S之前
+ \* j8 ^6 a, m  a3 x* D4 |) S3 Q先來看看bootloader, {9 I( |1 U* E+ `) z
當板子通電,最先被執行到的通常是bootloader* }5 M# A& n2 @" j  X% Z# f
透過它才有機會去改變載入過程, ]) A6 y. I1 E& O) B, Q1 u
例如更換這次使用的kernel image$ r3 d7 O+ d* @  U% G# B$ p  n8 t8 b9 S
或者是選擇要用tftp download image還是從flash上的某個image跑起來
8 n6 H) z: w# J( Dkernel為了讓這個變數盡量單純5 ~' j. n3 F( B0 o) w
因此linux也有限制bootloader必須在進入到kernel之前必須設置好的狀態: [$ @. f4 Y$ g7 z. }

6 m: B' }; m! l' O! G4 ?- v8 m1. r0 = 0& E, Y6 N8 J" U; F" F: q
2. r1 = architecture ID4 \9 |9 _1 J; j
3. r2 = atag list9 W- F( A+ {: k" l" {& y& b
4. mmu off
  E2 ]" Q* t$ D. h6 y. A5. I&D cache off
2 b* }# u8 _( B1 G! H, g6 q
3 {& P5 p3 c, i# ?, \% {8 e如此一來kernel就可以在已知的狀態去講好的暫存器拿資料,有了這個概念有助於看head.S。
9 u' |% V& G3 s5 s我們首先來看一開始的程式碼進入點
  1.     114 start:
    . H( }. f. l5 l1 S) W7 d
  2.     115         .type   start,#function! i! @7 Z5 l! m) n! D% G4 G
  3.     116         .rept   8
    / f/ U9 T& m2 Z3 S
  4.     117         mov r0, r0
    5 O, G) b- u8 p0 k, _
  5.     118         .endr  _/ X$ K4 I2 c( J
  6.     119- ^, y1 p. x6 \
  7.     120         b   1f7 \! r; W# h2 J  D
  8.     121         .word   0x016f2818      @ Magic numbers to help the loader% E) ^2 f# i) u$ F# r1 S3 @
  9.     122         .word   start           @ absolute load/run zImage address
    / ~$ K$ x2 K: _, A% U
  10.     123         .word   _edata          @ zImage end address% m5 X. i0 R6 Z' @9 O! a
  11.     124 1:      mov r7, r1          @ save architecture ID3 X$ q9 Q4 w' g! [1 I
  12.     125         mov r8, r2          @ save atags pointer
複製代碼
line 116~118, rept = repeat, endr = end of repeat, 意思是將move r0, r0的程式碼7 _" P/ Z" P- I! @( @
重複八次,也就是說build成kernel image的時候這邊一開始的code會有8個指令都在作
9 `$ u0 S* P" ?5 @9 f6 S3 U『move r0, r0』的事情,很怪!!還看不出是做什麼的。可能之後會看到如何被運用。# a* O$ i# ?* W1 d6 u
(有些文章寫說是作出中斷向量表的空間,我們這邊先不預先作猜測~)1 T; C5 x# C! p" e
! G0 |- [2 N" J0 Q3 L/ y/ p$ _+ t
line 120, branch到1的地方,f是指forward的方式找branch。
/ p8 U% ?" Y$ N4 `+ I9 s/ `  p5 G. R; {: i1 R+ Y, C6 Z
line 124, 125, 分別將r1, r2的資料丟到r7, r8存起來,回想兩件事情。
- T) N& S- E8 N2 r6 W. |8 v8 |  h7 H1. init.S執行的過程中,始終沒有用到r1, r2,那r1, r2的資料到底放著什麼東西。2 d7 S1 w' g' y. R
2. 一開始我們提到,bootloader會預先設定好狀態才跳到kernel,原來!!
# L9 A% P8 v3 y' B2 ^6 }) y$ R$ {/ }r1, r2就是bootloader準備好的。  6 e' N  E6 r( O9 U

. q  f! F/ T! w) ~7 W, v- P  S這讓我想到一個問題,假設我們不想跑bootloader,是不是可以寫一小段程式碼,直接將
; Q' S" V2 d" w2 Y狀態設置好,就直接進入linux kernel呢??
. q& R$ U* s1 U. P  p3 [  Q0 t  ^0 I7 _& K* Q6 U
line 121~123, 純粹將一些資訊記住,.start就是 kernel 起始位置,這邊看起來是* S: \# F! [5 v- L9 K
忽略掉init.S和initrd.S佔去的位置,直接將.start這個section當成kernel image的開始起點。
. _/ L2 j2 ~+ d2 f. ^
, n+ Q6 `7 O5 i3 D, d* u接著繼續往下看(我們預設arch已經超過v2,現在應該大多是v4以上)
  1. 133         mrs r2, cpsr        @ get current mode7 \# F; y' I9 p4 w8 H/ E8 j
  2.     134         tst r2, #3          @ not user?
    ; W2 o$ s  T& x! ]
  3.     135         bne not_angel/ m: W7 K: ]/ G7 ^& f" m6 m/ D
  4.     136         mov r0, #0x17       @ angel_SWIreason_EnterSVC3 ^1 n8 v, q3 B' L' m) {
  5.     137         swi 0x123456        @ angel_SWI_ARM
    / b9 U" g5 q4 A8 e/ P
  6.     138 not_angel:
    5 ^' P) M7 |8 y* K9 A1 \& v7 q1 t
  7.     139         mrs r2, cpsr        @ turn off interrupts to+ ]9 D* b/ W2 R2 Z0 ]: K
  8.     140         orr r2, r2, #0xc0       @ prevent angel from running
    ) e! z+ r3 V& N$ d8 }
  9.     141         msr cpsr_c, r2
    / n" N  ~) \+ i5 n7 l& I4 P/ D
  10.    
複製代碼
line 133, mrs 是特殊用來讀取cpsr和spsr暫存器裡頭紀錄processor模式值的指令,這兩個reg是" i! h: q7 K( Q& e
用來控制和表示processor目前狀態的。/ V; M' G  D4 m  V( }  d6 w
1ine 134, tst = test, 看看r2是不是等於3。) n: o# a, L, X; R/ G8 x
line 135, r2不等於3的話就跳到 not_angel 這個地方開始執行,記得以前有個angelboot可以用
) i6 ^" L" k# k, g! v來boot armlinux,應該是angelboot會特別跑在3這個mode。
/ f3 }5 N& S' u% dline 136, 137, 用來觸發angelboot裡頭的swi的function,作用應該是要切回去SVC mode。SVC mode7 ]( H: z1 ~" ~) c8 q: a% M
是一開始ARM processor預設執行模式。
' \2 w/ x! j3 S' R+ Q3 x
. ?; C  g: s7 h$ R7 L& p$ }* lline 139~141, 用來關掉interrupt,避免被中斷booting的過程。(因為複雜一點的bootloader通
: e* w6 ^5 Y( n. l' r常會已經support很多driver,中斷也很頻繁。
作者: jacky002    時間: 2008-8-9 07:40 AM
補充資料 - ARCH: ARM11 -> v5) x* q' L; o0 @: P
可參考' Q( n) }% z+ w# O( C6 ~! T! K
http://tech.digitimes.com.tw/pri ... FE2482571DD006E9DC5- x* e1 ?3 x% g* ~( T$ T, k3 G( |
/ Y0 d/ k! E1 u& o1 o0 {4 J- p9 T
建議可在加上UBoot or Redboot的部分,應該可以造福更多初學者。
作者: gogojesse    時間: 2008-8-9 11:40 AM
原帖由 jacky002 於 2008-8-9 07:40 AM 發表
" z# W1 g6 q' X7 W- Y: d* q建議可在加上UBoot or Redboot的部分,應該可以造福更多初學者。
) K1 w5 x/ D0 j# [% O/ I; a
# c0 U/ R& V- j* q+ @% s4 y
看完kernel應該會花上一些時間* l4 \/ v$ t# p% F
看看有沒有哪位大大要認領
* ~  n. T; f+ K5 h開一篇bootloader的文章   
( T9 u) X. C4 K$ h4 q: }1 |/ D) v6 p# d
另外,有人要trace x86 or MIPS的架構應該也不錯
# Q) w1 Q( c! ~& Q7 l/ t  i這樣主要的幾種processor都可以搞定
$ D" c3 {2 \( `0 @3 s* H這樣要跨平台  跳槽也容易許多
作者: gogojesse    時間: 2008-8-11 12:07 PM
程式繼續往下跑
8 ]# U! |  N9 w  M2 C6 L$ R! C這邊插點符號跳過文繞圖
: C+ P+ V( ~* b: }# ]& w.
, Z7 z8 Q, F3 B2 \.5 E- c- N+ |2 P( s. O1 K
., q+ O+ b3 x- Q# m8 A, j
.& r# x4 {; o* d9 ~' C6 B: B
.
( l2 i$ F) [. J1 g6 M2 U' a, B.. G$ [. Z0 o) |& O$ s& t
.
# ]2 f5 G5 [5 x- ^2 R.  W& q; D8 C9 Y( Y: F1 k
.$ ?' i6 g; |9 i( {3 m9 K& d7 [" x
.
  1. 157         adr r0, LC06 c4 e# {  t( P. {& O1 g( j2 ?
  2.     158         ldmia   r0, {r1, r2, r3, r4, r5, r6, ip, sp}
    # H' x# |& e4 S2 g0 z- \
  3.     159         subs    r0, r0, r1      @ calculate the delta offset
    5 r$ Q/ e/ [6 w3 |$ `
  4.     1602 l5 _& Z8 H/ R; W
  5.     161                         @ if delta is zero, we are+ G, l1 @$ c1 ^4 Q% Q2 t, [7 F
  6.     162         beq not_relocated       @ running at the address we
    + G  p- O+ Q( E( g* G8 M
  7.     163                         @ were linked at.
複製代碼
  1.     288         .type   LC0, #object
    * {  g- \* T' i, G) h" `: }. m
  2.     289 LC0:        .word   LC0         @ r14 D6 C* ]4 a2 \
  3.     290         .word   __bss_start     @ r28 C+ a8 ?' V+ c6 b
  4.     291         .word   _end            @ r30 ^6 i  O, X! v
  5.     292         .word   zreladdr        @ r4! _7 h# @! i0 x$ _
  6.     293         .word   _start          @ r5) r$ M- _5 W/ y  ^! B; x+ s0 @$ v0 E
  7.     294         .word   _got_start      @ r6$ N) A% J9 @! t8 |
  8.     295         .word   _got_end        @ ip
    . N5 @9 d% B. w1 z
  9.     296         .word   user_stack+4096     @ sp; T7 p& ?; m& h! Y4 S/ m
  10.     297 LC1:        .word   reloc_end - reloc_start; s) E! c/ a# Q3 m
  11.     298         .size   LC0, . - LC0
複製代碼
line 157, 將LC0的位址當作值放到r0。; _- Z# R) I; }. w9 w- W& W) F
line 158, 從r0指到的位址開始,將值依序讀到r1, r2, r3, r4, r5, r6, ip, sp, 其中 ip=r12, sp=r13* Y- w$ e6 B! k2 P
line 159, 將r0-r1,這邊的意思是說r0是真正被載入到記憶體上的address,r1是被compile完就已經決定好的位
2 J& v2 N8 [7 R  \3 ]$ P址(也就是line 289中LC0這個symbole的address),兩個相減,剛好可以算出『compile好』跟『被load到位址』
, X: F: ^' m8 Q% R* z之間的offset,這樣做有什麼意義? 繼續往下看。/ X% m, Z, I' [1 B4 d  G

4 s7 i2 h7 N8 ~& _: P1 E% Vline 162, 如果相減等於0,表示載入的位址和complie好的位址是一樣的,那程式碼就可以被直接執行,要是不為0
" n" X/ ]9 Q# z的話,表示compile本來以為這些執行碼會被放到 r1 的位址,可是卻被放到r0的位址去,這樣一來,有一些預先compile好的程式碼,可以會找不到一些symbol的所在位置,因為整個image被load到不對的offset的地方。那...
2 D4 x+ x% w  s0 ~4 l% q怎麼辦勒??
5 G$ m: [2 S1 n0 u. U  i0 _( q, H2 d4 q' Q2 H2 M. I" a8 R$ W
往下看
  1.     172         add r5, r5, r04 J, l) k/ X8 R, Q
  2.     173         add r6, r6, r0
    / I9 O( S5 V% o& D* {
  3.     174         add ip, ip, r0
    . r" f: B$ y0 C8 u

  4. ; j% X( \* c2 v+ B1 X* l% Z/ R# x) I
  5. 202 1:      ldr r1, [r6, #0]        @ relocate entries in the GOT
    , E" E6 l3 n# u! q* Y9 t9 m2 j/ v
  6.     203         cmp r1, r2          @ entry < bss_start ||# A0 T* j4 }+ m" H! O; W
  7.     204         cmphs   r3, r1          @ _end < entry
    % [* v: q( I4 @# A# I8 }+ @, w
  8.     205         addlo   r1, r1, r0      @ table.  This fixes up the
    ( L2 Z! d; Z, Z$ k$ s
  9.     206         str r1, [r6], #4        @ C references.
    $ M$ K  [4 z  r, \3 d3 Q$ h, m8 ^. ~
  10.     207         cmp r6, ip
    ; J* Y2 x$ |0 e3 ^
  11.     208         blo 1b
複製代碼
line 172~174, 將r0這個offset,加到r5, r6, ip,也就是r5=zImage base address, r6=GOT start, ip=GOT end. GOT全名是global offset table, 它是ELF format執行檔裡面用來放一些和位址無關的code的地方。詳細的東西可以參照http://www.itee.uq.edu.au/~emmerik/elf.html。總之,可以看得出來我們將一些位址加上: k3 M: w5 @) Z
了offset,很明顯的是因為我們載入的位置跟原本執行碼所預期的位址不同,因此必須做一些relocate的動作,若是不% H. }) ]* a3 E' [3 K. y
做的話,很可能程式碼會拿到不對的資料,我是jump到錯誤的地方執行。
5 R& w* J7 O" ~3 |: {' T
* @3 d; S) j4 bline 202~208, r1指向GOT table start,在沒有寫錯到bss區塊的情況下,將GOT裡面的資料都作relocate的動作。4 R3 q7 c) c) y
line 203, 204,應該是用來避免r1只到bss區塊。關於BSS也必須參考ELF format的東西, BSS是用來放置,未經初始
- g7 O3 ~1 R6 f# d; i; [. v化的變數的地方。# _2 E/ |( e, K* a# Y
6 g8 J9 D/ f1 \5 n! Y
以上,我們發現kernel意識到自己被載入到某個地方,並且查看被載入到的地方是不是和compile
  u, o3 q& H: y- d# v$ btime決定一樣,不一樣的話,自己手動修改一些需要做offset的資訊,等於是手動作relocate的事情。
作者: gogojesse    時間: 2008-8-14 07:02 PM
放了兩天假出去happy  
, g4 O/ b. Q' I/ R1 x接著繼續trace
  1.     211 not_relocated:  mov r0, #0
    " ^: h9 A/ L, t' O' R
  2.     212 1:      str r0, [r2], #4        @ clear bss
    , m, c- ]  B5 b. o1 N# F# R
  3.     213         str r0, [r2], #42 S! j6 a9 R5 o
  4.     214         str r0, [r2], #4' @3 Z0 {3 `& l
  5.     215         str r0, [r2], #4
    8 y* W. ]& _, {1 c5 ?6 ~
  6.     216         cmp r2, r3  @9 C8 r( `% U4 ~. W
  7.     217         blo 1b$ t! W3 i9 Q' s1 w* V
  8.     218
    + l7 }+ v" w0 l
  9.     224         bl  cache_on
複製代碼
恢復記憶一下,上次trace到kernel做了一些判斷,如果被載入的位址和compile time決定的位址不同,就會; l/ M/ Z0 V: a  D1 b
自己做relocate的動作,將一些ELF binary的特定pointer和value加上offset。那做完初步的relocate之後要做什麼?6 r# j1 Z& E2 M
; U0 v! n! j3 Z4 \; q
line 212~215, 都是做store的動作,把r0存到r2所指到的位址,做完之後r2=r2+4。r2= bss start的位址. ) K' r3 z; T, P4 S  P! e
換句話說,開始將bss裡頭的值都初始化成0。- f! [& A2 f% W$ z
lin3 216, 217, 確認一下是不是到了bss的底部,不到底部的話,jump到line 212繼續做搬移的動作。
: W! u7 r2 A% `# _; }9 @- ]
; }: p) J5 x. K: @# Y' fline 224, 做完bss初始化,jump cache_on
  1.     328 cache_on:   mov r3, #8          @ cache_on function
    . m4 {" b% M! n% [) m" k
  2.     329         b   call_cache_fn9 v% m% o+ S" ?3 Y$ F
  3. $ ?' L8 a+ _2 `; r" B. T4 O
  4.     537 call_cache_fn:  adr r12, proc_types8 g+ Y. @2 D% T, U
  5.     539         mrc p15, 0, r6, c0, c0  @ get processor ID
    7 g: x( @$ M( W" O

  6. 3 n/ |3 Q2 G9 a1 O% l3 I
  7.     543 1:      ldr r1, [r12, #0]       @ get value" d) v1 j+ Y! p
  8.     544         ldr r2, [r12, #4]       @ get mask
    - U2 @) p! B1 s
  9.     545         eor r1, r1, r6      @ (real ^ match)
    5 c7 D, I) P) D! Y
  10.     546         tst r1, r2          @       & mask) t) `* A- N1 e8 j- _1 E: _2 a: t
  11.     547         addeq   pc, r12, r3     @ call cache function7 R7 L0 m" [. ~+ s2 F5 Q
  12.     548         add r12, r12, #4*5
    3 \' S( F0 p1 A! {* z
  13.     549         b   1b
複製代碼
line 328, 將r3填入8, 不知道r3會拿做什麼用,繼續看。
) t5 J: d! p7 m1 @6 Rline 329, jump到call_cache_fn。5 \7 U3 J- |$ P1 C! ]& [4 k
line 537, 將proc_types的位址讀到r12。
# r8 _, |8 t3 J6 s' Z& k  F, ]line 539, 將coprocessor裡頭的CPU id讀出來放到r6
9 K3 l5 H: h' }0 iline 543, 544, 將r12所指到的第一個位址的資料放到r1, offset 4bytes的資料放到r2,我們可以先觀
) b3 h5 H# R2 {# U1 }) [5 {  x察一下proc_types的長相(如下),註解上面寫了很多arm的家族的名稱,例如arm 6, armv5te等等,而且不
. Z; z( h  |- @( V6 d難發現都是先兩個.word,然後跟著三個『b xxxx_cache_xxx』,感覺很像是一組一組的資料。
% B9 [6 w) m3 _, H0 eline 545, 546, 將r6裡頭的CPU ID和讀出來的r1做exclusive OR,並且取mask,看看是否相等,相等的
& _. h$ C, [4 ^/ {2 [" x. z4 f5 h話,就將pc設定r12+r3。換句話說,就是用CPU ID去確認值是否相等,值相等的話,就jump到r12+r3的位址。
: ]! U) |$ K# n& yline 548, 549, 不相等的話,就把r12加上5x4byte的offset跳回去繼續找。
/ \* }" c7 C1 j, ~) @* Q整理一下,這邊的程式碼就是去proc_types的地方,比對CPU ID,比對成功的話,就呼叫該筆資料裡面的. Z4 A. z( R' M, A" ~
cache function,至於呼叫第幾個function,就由r3控制,那所有CPU對應到的data structure就* u. L+ h# H1 c! M# H: S" d  E
從proc_types開始。' G( \! f+ a9 X

! c0 C0 K) B4 g4 p! a' x以ARMv5TE來說,r3=8,就剛好是cache_on的function。所以我們知道如果我自己發明了一個新的ARM CPU,也弄了一個新的id,這邊就需要修改出相對應的CPU的infomation,不然可能會找不到CPU ID。
  1.     566 proc_types:
    1 N3 n4 n# `# f- A* r/ Q
  2.     567         .word   0x41560600      @ ARM6/610% @7 k/ ^. a% L1 y: w" _
  3.     568         .word   0xffffffe0
    ( k$ m5 w' D2 z$ F
  4.     569         b   __arm6_mmu_cache_off    @ works, but slow% Z' i: V. y9 t4 _+ ~6 u
  5.     570         b   __arm6_mmu_cache_off
    ) I& g- x$ }9 f; z1 C- {' w
  6.     571         mov pc, lr! m. N5 q! j6 s! {
  7.     ......$ h. S8 U$ A! j  ~% _, Y2 W0 L
  8.     640         .word   0x00050000      @ ARMv5TE7 l: h: S9 }) _' {, c* x0 w+ m8 U
  9.     641         .word   0x000f0000. `9 \5 q* {; E
  10.     642         b   __armv4_mmu_cache_on) x. Y. J! q* o% f$ `5 Z) S
  11.     643         b   __armv4_mmu_cache_off5 V/ c- g( \7 G/ r
  12.     644         b   __armv4_mmu_cache_flush
複製代碼
到這邊我們,找到了CPU對應的cache on的function,必且要準備呼叫它。
作者: rogerho    時間: 2008-8-28 10:56 AM
很棒的分析....讓我能有機會可以瞭解bootloader的一些流程.............感謝
作者: gogojesse    時間: 2008-8-29 10:13 AM
很棒的分析....讓我能有機會可以瞭解bootloader的一些流程.............感謝
( |) w& O0 y- O- D( W* [. h
* ^0 v5 M6 q$ p
謝謝   9 M: W6 _0 D) ]5 \. u5 ^
最近突然忙起來+ h' ~" J! l0 d1 }, \! \0 r3 \' J
改天有空再繼續study....# }: @0 v$ H. n9 f$ \+ l  S
  |" R3 P9 a+ u% f! I
另外,這篇是kernel booting的過程的程式碼,應該不能稱呼bootloader,不過/ H6 [, D1 K! S# M; Z) ^- n
有些概念跟bootloader差不多,可以幫助閱讀bootloader的code就是。  
作者: gogojesse    時間: 2008-10-7 12:43 PM
忙了好一陣子∼! r1 ~8 D% x: H' {
之前trace到 ./arch/arm/boot/compressed/head.S的 line 224
7 x2 |! D' a7 x" k% h  N呼叫了cache_on之後就沒寫了
5 f, C. @8 }; R# b0 i6 ~2 L7 H現在接著開始! s4 Z( @) e' o/ V/ }
$ j/ p3 z% M; j9 }
首先我們偷看一下code,
! {) e9 G0 V9 I! Y1 ^2 |line 226, 將sp的值放到r1。
5 S; f- K, [' r" Y) A+ b& j$ U, @$ @line 227 將sp的值加上0x10000放到sp。( H, s* _4 H5 d- H6 \( i  }

+ ~' ^4 t! D8 X+ D  d& l為什麼kernel之前花了一些功夫將自己relocate到某個位置之後,要把cache打開,然後要開始對stack pointer(sp)做動作?目前還看不出來,所以接著trace下去。3 q2 ^+ T/ r3 n7 P0 Z

; B. F. d7 ^% R4 Z( g  t( L! r% t( _  fline 238,比較r4和r2的值,r4的值從line 158載入之後就一直沒被用到過,這個值是從一些makefile或是被makefile include進來的,然後在linking time的時候會被帶入,每個平台不一定一樣,通常你可到./arch/arm/mach-xxx/Makefile.boot去設定,這個值是用來指定kernel應該要被load到哪個位址上面執行。以omap1來說,, A' E- }2 k7 N: Z! a: `/ c( F
zreladdr-y       := 0x10008000
( R: b) I7 |0 g; I( y0 a  H就是表示kernel會被載入到 0x10008000 的地方。這邊將r2和r4比較的用意是看看sp+0x10000之後會不會超過zreladdr的位置,應該是怕stack爆掉了會蓋到kernel的地方。(記住我們現在的kernel其實還在壓縮狀態,zreladdr是指解壓縮完要開始執行的狀態。)9 N5 i3 |' d$ Y/ S' T3 ^

. l  Y" C8 Q" k3 o! mline238~line243, 比較了r4和r2,假如不會蓋到,就會跳到wont_overwrite去執行,假如會蓋到,就看目前sp到之後解壓縮image位址之間的距離有沒有比image四倍的大小來得大,假如有,表示空間還夠用,還是可以跳去wont_overwrite去,假如不到四倍大,就跑到line 262去把kernel搬到遠一點的地方,試看看能不能正常boot起來,line262先不做解釋,一般來說位址設錯的話,這邊的correction失敗的機率還是很大,著眼在correction的意義不大。所以我們就直接跳去wont_overwrite吧!
  1.     226         mov r1, sp          @ malloc space above stack5 M; _& C8 v/ Z9 j/ y1 z3 B( S
  2.     227         add r2, sp, #0x10000    @ 64k max
    3 C& c9 n0 Q6 w1 Z/ m9 {

  3. " L0 @! L) c6 J+ O' A- e
  4.     238         cmp r4, r2
    6 Z2 J! _* T/ H7 k% x/ R% w4 s
  5.     239         bhs wont_overwrite
    - M" H4 L5 l# U: T! N, {
  6.     240         sub r3, sp, r5      @ > compressed kernel size
    - e2 a1 q$ J9 [
  7.     241         add r0, r4, r3, lsl #2  @ allow for 4x expansion
    ! @; B( ~6 W' A5 @
  8.     242         cmp r0, r5
    3 ]. `  p7 R( H' J& \6 J) _
  9.     243         bls wont_overwrite# e% b5 _/ J- j
  10.     2445 {, @# p# O; B/ o- u
  11.     245         mov r5, r2          @ decompress after malloc space( Z- ?, N4 A. N
  12.     246         mov r0, r5
    ! R) @; t3 H* ]$ L
  13.     247         mov r3, r7
    1 I' q# B- i7 D6 G) u+ K: y# b& K
  14.     248         bl  decompress_kernel* ~; t" V* [) j5 O: ?! t
  15.     249" _4 R5 D# t3 F9 ^+ T" s
  16.     250         add r0, r0, #127 + 128  @ alignment + stack0 r  q7 H- f9 r
  17.     251         bic r0, r0, #127        @ align the kernel length
複製代碼
跳到wont_overwrite之後,當然就是要開始把kernel解壓縮,2 y# `4 @, c) {+ m: b. Z
line 283,把r4搬到r0,r4就是我們剛剛說的kernel被解壓縮之後的位址。(也就是解完之後應該要執行的位置)8 a" c: c9 g9 H* _; u
line 284,把r7搬到r3,r7從一開始讀進來之後,也沒用過,理論上是architecture ID。+ u, ~2 v0 Y# {% D+ ~! v
line 285,是跳到decompress_kernel,這邊我們發現decompress_kernel是被定義在misc.c檔,所以這是第一次從assembly code跳到c code的地方。這樣一來我們就知道原來剛剛要把cache打開和設定好sp的用意,原來就是為了要執行c code,因為c的程式碼有固定的執行方式,會需要用到sp,這部份可以參考『procedure call standard for the ARM architecture』,這也是r4和r7被搬到r0, r3的原因,因為r0~r3是用來傳遞C function的參數用的,r0就是arg0, r1=arg1, etc.
  1.     283 wont_overwrite: mov r0, r4
    ) c' {8 x- q- m( G/ m3 N( S
  2.     284         mov r3, r7+ M+ B5 |3 u, ~* n4 V
  3.     285         bl  decompress_kernel
    ( {6 Y4 _5 x' }. G8 H" X2 U2 e
  4.     286         b   call_kernel
複製代碼
偷看misc.c
  1. 346 decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr, int arch_id)
複製代碼
果然r0~r3就是的參數。
作者: gogojesse    時間: 2008-10-7 01:01 PM
由於解壓縮不是我們的重點; T# i1 D( ^3 [6 t* b& R
沒有trace
1 n& U4 W+ `; {3 B; @, ^假設一切都順利
1 O, r/ T. {2 p- Udecompress_kernel結束後& B! G0 ^: `" m5 S6 w. n$ M& D
我們就得到一個解壓縮完的kernel放在r4指向的位置$ `( [% Z0 m5 q2 Y
line 286,會jump到call_kernel,如下:
9 O9 U$ a" R% Zline 516, flush cache, \7 N0 h, V9 q6 c
line 517, 關掉 cache6 p) x2 l: N1 s" l% W5 l# ?. j
line 518~520,將r0, r1, r2分別填值。
. u6 c* M( C% N* p! iline 521,將program counter指到r4,也就是解壓縮的kernel的一開頭。
2 e& t% J# E0 H& i7 l3 `# r5 Q, ?3 y- x' l9 B; r2 M' w, u- C: Z
到這邊我們終於結束head.S的工作,解壓縮並且跳到另外一個object code的開始。跳到解壓縮的開始位置,究竟會進入哪一個function?
  1.     516 call_kernel:    bl  cache_clean_flush
    9 T* q$ O3 v) C& E  c( p6 D
  2.     517         bl  cache_off
    " ^) t  x- A- x  r3 H6 e2 R
  3.     518         mov r0, #0          @ must be zero
    4 ]. X% u" }4 q
  4.     519         mov r1, r7          @ restore architecture number( b+ {% Y7 o9 Y3 V% q
  5.     520         mov r2, r8          @ restore atags pointer
    9 s! K6 R9 t6 d& Y) l
  6.     521         mov pc, r4          @ call kernel
複製代碼

作者: kkbbs    時間: 2008-10-11 10:39 PM
很棒的分析....9 S- o; `9 P2 n* L; y& E8 e
非常據有參考價值6 ]3 x& C- m+ g5 F) u0 S) n
感謝大大分享    感恩
作者: gogojesse    時間: 2008-10-13 10:15 AM
原帖由 kkbbs 於 2008-10-11 10:39 PM 發表
: I: w) ?! B6 R) }5 }, {- q很棒的分析....
! o3 v- N4 T$ E" [2 q. w非常據有參考價值
- g/ s: a( U/ C; H/ b( Z# U感謝大大分享    感恩
$ X/ u3 Q4 c# ]& Q4 [- e+ p  L
: I& B: j/ {  h3 ?* J1 H$ q2 a3 Q
謝謝   - A( N! ~4 L7 m7 h$ Y& A/ u! w
有哪邊寫錯或是有怪怪的地方
& V& G- A/ o, A# M0 t( z3 N歡迎提出來一起想想...




歡迎光臨 Chip123 科技應用創新平台 (http://www.chip123.com/) Powered by Discuz! X3.2