Chip123 科技應用創新平台

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

作者: gogojesse    時間: 2008-8-8 04:01 PM
標題: trace linux kernel source - ARM - 02
開始跳進去head.S之前8 J  `) M) k: g) u7 c- f- \
先來看看bootloader
# R2 ?( B* x% `" _4 r. Q當板子通電,最先被執行到的通常是bootloader
" \' K9 A. |0 ?' J  T- M透過它才有機會去改變載入過程
" f( {5 E: Y3 M& A例如更換這次使用的kernel image9 P1 d8 Q! G6 d) z
或者是選擇要用tftp download image還是從flash上的某個image跑起來
7 O" z5 W- Y. \6 L. w5 ~& nkernel為了讓這個變數盡量單純
  D9 Y7 C7 i/ }因此linux也有限制bootloader必須在進入到kernel之前必須設置好的狀態  W/ q, ?1 l5 T3 }+ j9 Z
, m3 y7 y3 b( Y" p2 }; r
1. r0 = 0
  b0 x7 [4 Z7 M; `' N2. r1 = architecture ID* a+ w0 U5 k( k
3. r2 = atag list- g" r* x6 Q& z7 z
4. mmu off
- S. e3 ?- g* U- K! w5. I&D cache off! e) B- Z; o8 ]4 W( o2 X

6 K& N1 D4 s- o0 Q如此一來kernel就可以在已知的狀態去講好的暫存器拿資料,有了這個概念有助於看head.S。( X( S- ]( _8 y+ P
我們首先來看一開始的程式碼進入點
  1.     114 start:3 r% g  D8 D) x
  2.     115         .type   start,#function7 T3 N  }* H- a5 R
  3.     116         .rept   8" g1 w9 C- m5 _5 v! f8 s% r
  4.     117         mov r0, r04 j3 N) Q2 Q/ k0 t2 Y9 ]
  5.     118         .endr
    $ l  b5 n9 P8 I
  6.     119
    , H6 O  [4 Z0 O" i+ ^! H
  7.     120         b   1f
    / P; H; i9 C6 H
  8.     121         .word   0x016f2818      @ Magic numbers to help the loader
    + [$ Z; M& ~# `" w7 F- ^# H+ C. e
  9.     122         .word   start           @ absolute load/run zImage address
    + u. P- g7 K6 e7 _
  10.     123         .word   _edata          @ zImage end address
    ' U. o! g% h4 ]4 Q
  11.     124 1:      mov r7, r1          @ save architecture ID1 x' X% E2 K$ s% M1 ^
  12.     125         mov r8, r2          @ save atags pointer
複製代碼
line 116~118, rept = repeat, endr = end of repeat, 意思是將move r0, r0的程式碼6 f, a0 n% F& C, m4 \8 V
重複八次,也就是說build成kernel image的時候這邊一開始的code會有8個指令都在作
. b6 v) x% R: Y! F『move r0, r0』的事情,很怪!!還看不出是做什麼的。可能之後會看到如何被運用。
8 p" R- @% X; r; {7 S. u0 C, N(有些文章寫說是作出中斷向量表的空間,我們這邊先不預先作猜測~)
/ e3 R7 K* i8 R! r; f- x0 C, y* H+ c# z% H0 A9 D
line 120, branch到1的地方,f是指forward的方式找branch。
# C. b7 q6 N+ g
2 G2 o4 x" Y+ R8 `5 r$ ^5 vline 124, 125, 分別將r1, r2的資料丟到r7, r8存起來,回想兩件事情。/ F" d6 j8 a: C; z
1. init.S執行的過程中,始終沒有用到r1, r2,那r1, r2的資料到底放著什麼東西。1 s% m/ K+ i" B( [2 Z! u3 }" p9 C
2. 一開始我們提到,bootloader會預先設定好狀態才跳到kernel,原來!!
% A, L7 J: n/ Q8 x% F1 o# fr1, r2就是bootloader準備好的。  
  D& m. T8 `1 E1 M% p/ ^+ @- u$ |+ M- \/ d3 P
這讓我想到一個問題,假設我們不想跑bootloader,是不是可以寫一小段程式碼,直接將
1 c" q+ s" z8 ]0 v; X. F9 ]狀態設置好,就直接進入linux kernel呢??
2 Y8 ~: J8 I6 G$ n: l
0 H. j# S# y1 E+ @- Wline 121~123, 純粹將一些資訊記住,.start就是 kernel 起始位置,這邊看起來是
5 ~7 W5 a; v6 B' {忽略掉init.S和initrd.S佔去的位置,直接將.start這個section當成kernel image的開始起點。& B7 h3 c# k$ `! j$ t* v% ~4 w

: l2 Y: y) g- V6 U接著繼續往下看(我們預設arch已經超過v2,現在應該大多是v4以上)
  1. 133         mrs r2, cpsr        @ get current mode
    7 ^9 p/ W# v/ d  g
  2.     134         tst r2, #3          @ not user?3 q7 i5 q5 s) N$ E0 ]3 c
  3.     135         bne not_angel
    2 i' H8 P5 L0 e: c9 J
  4.     136         mov r0, #0x17       @ angel_SWIreason_EnterSVC8 c) y# T+ D, Y+ n
  5.     137         swi 0x123456        @ angel_SWI_ARM
    7 w3 R6 L7 K% ?) w6 r  _9 O
  6.     138 not_angel:
    . L7 c# A, e# M! |- C
  7.     139         mrs r2, cpsr        @ turn off interrupts to0 j+ k6 z9 v( D3 {
  8.     140         orr r2, r2, #0xc0       @ prevent angel from running
    ; z5 @7 R0 W7 N9 ]. m( w. f
  9.     141         msr cpsr_c, r2$ e- p7 W$ \7 V; h" Y4 ?
  10.    
複製代碼
line 133, mrs 是特殊用來讀取cpsr和spsr暫存器裡頭紀錄processor模式值的指令,這兩個reg是, }& A" f2 d. c' G) o
用來控制和表示processor目前狀態的。
1 \- U; B  C0 g2 O4 W/ s1ine 134, tst = test, 看看r2是不是等於3。8 c* S; P- @8 {& x) `7 F" x3 U
line 135, r2不等於3的話就跳到 not_angel 這個地方開始執行,記得以前有個angelboot可以用
  f3 l" t5 Z. y( X/ w& [/ S來boot armlinux,應該是angelboot會特別跑在3這個mode。6 d* Z" d- U7 f5 N/ `
line 136, 137, 用來觸發angelboot裡頭的swi的function,作用應該是要切回去SVC mode。SVC mode
$ g8 \$ t" S  G' U& u是一開始ARM processor預設執行模式。& C3 q( @/ f5 R1 X
0 m% i/ q7 V5 O. e" q! p4 H
line 139~141, 用來關掉interrupt,避免被中斷booting的過程。(因為複雜一點的bootloader通+ Z7 {# X, N1 i* a
常會已經support很多driver,中斷也很頻繁。
作者: jacky002    時間: 2008-8-9 07:40 AM
補充資料 - ARCH: ARM11 -> v5, h4 `& }" z% P) H
可參考$ V8 f. L) `% s  P
http://tech.digitimes.com.tw/pri ... FE2482571DD006E9DC5
/ B; {& w" ^) O2 N7 [0 \% T) q# x5 y1 d
建議可在加上UBoot or Redboot的部分,應該可以造福更多初學者。
作者: gogojesse    時間: 2008-8-9 11:40 AM
原帖由 jacky002 於 2008-8-9 07:40 AM 發表
; u$ y' V% q+ ]( ~" T建議可在加上UBoot or Redboot的部分,應該可以造福更多初學者。
0 X0 q. ^6 m' p- ?( V- x. r9 t
1 _0 S- b3 @1 U* G- Q5 U- S& W
看完kernel應該會花上一些時間
. c, {+ p  z6 }4 }看看有沒有哪位大大要認領
8 D. c2 l6 d$ O# e2 ]' p開一篇bootloader的文章    8 G- w( ]  h4 a' y; w7 {7 N7 c

8 `) s( l+ S( i+ B8 u另外,有人要trace x86 or MIPS的架構應該也不錯
; `" x! W$ B: g( K' D% P這樣主要的幾種processor都可以搞定5 m0 L! u) {) \1 T$ z- J
這樣要跨平台  跳槽也容易許多
作者: gogojesse    時間: 2008-8-11 12:07 PM
程式繼續往下跑
* B( }  N. J/ C! ^這邊插點符號跳過文繞圖
2 B$ ~$ R2 i: p1 j.% _. R  ~# g9 I1 y2 r
.0 {/ }" V1 s* E  Y, P
.
8 K; _$ d" _1 |6 I3 ].2 n% ^$ d9 e2 b' w
.
! l' J+ o$ d2 f( }- j* ^.
; m- n8 R6 L4 \; u" x5 K+ N0 F.
1 i7 y' N9 K& V.& |% `! C9 P; ]) ?3 ~! P8 R
.4 A; p& m2 \6 @1 M8 i; {
.
  1. 157         adr r0, LC03 L5 [; I5 N9 f' L- J; a6 }1 z
  2.     158         ldmia   r0, {r1, r2, r3, r4, r5, r6, ip, sp}3 H! D: y& c/ H7 L
  3.     159         subs    r0, r0, r1      @ calculate the delta offset3 J- |" `+ p* ~# o
  4.     1600 S+ i& K/ V4 D  X+ a. d
  5.     161                         @ if delta is zero, we are2 W8 |; p  j. C8 z. c; H
  6.     162         beq not_relocated       @ running at the address we) f9 y/ b/ y; A
  7.     163                         @ were linked at.
複製代碼
  1.     288         .type   LC0, #object
      i3 [4 v; [* F$ T4 T5 D
  2.     289 LC0:        .word   LC0         @ r1
    ) u$ b5 |/ I5 r6 w8 p
  3.     290         .word   __bss_start     @ r2
    ( Z$ P0 t* B9 a# X' y
  4.     291         .word   _end            @ r3
    1 J8 e& v; @1 I: j
  5.     292         .word   zreladdr        @ r4
    & n  j0 y  ?; j, t: z3 h- z
  6.     293         .word   _start          @ r50 f, W4 u& k* m- h: V: p5 h' B
  7.     294         .word   _got_start      @ r6% ?  R* ]2 J2 v9 c
  8.     295         .word   _got_end        @ ip
    $ W. D8 S9 z+ a3 }% `
  9.     296         .word   user_stack+4096     @ sp) H# X- `+ e) H! j, L; p
  10.     297 LC1:        .word   reloc_end - reloc_start
    6 `" R3 a, ~) ~4 `3 ^7 \# D2 A
  11.     298         .size   LC0, . - LC0
複製代碼
line 157, 將LC0的位址當作值放到r0。
- o0 X5 j. \+ J6 [line 158, 從r0指到的位址開始,將值依序讀到r1, r2, r3, r4, r5, r6, ip, sp, 其中 ip=r12, sp=r13
- u; {5 B  g' Z' {line 159, 將r0-r1,這邊的意思是說r0是真正被載入到記憶體上的address,r1是被compile完就已經決定好的位; h1 E/ k7 N* E9 w2 D% j
址(也就是line 289中LC0這個symbole的address),兩個相減,剛好可以算出『compile好』跟『被load到位址』) }0 u( P, A% I2 l4 f9 u5 X7 Z
之間的offset,這樣做有什麼意義? 繼續往下看。
# f6 s  f" i8 c8 `' C% [: o8 Q
" W; F9 ]3 y0 ^line 162, 如果相減等於0,表示載入的位址和complie好的位址是一樣的,那程式碼就可以被直接執行,要是不為0
6 q9 g! b" C7 S" o# N. ]的話,表示compile本來以為這些執行碼會被放到 r1 的位址,可是卻被放到r0的位址去,這樣一來,有一些預先compile好的程式碼,可以會找不到一些symbol的所在位置,因為整個image被load到不對的offset的地方。那...+ {) y; j9 E" U+ f5 q
怎麼辦勒??
- L; M* v$ V; {. R$ y! |/ Z6 d; Y, r4 E' H) J
往下看
  1.     172         add r5, r5, r0
    1 s) L2 X6 H3 L2 R! J) @/ @
  2.     173         add r6, r6, r09 I+ t5 K5 h4 f6 C& m, D$ L5 }5 l
  3.     174         add ip, ip, r0
    2 @: B1 P' E& |/ l+ o5 d

  4. 6 I8 S/ Y- |0 Q: g
  5. 202 1:      ldr r1, [r6, #0]        @ relocate entries in the GOT
    0 r  O4 k7 U) g, U& r' O' g- c( ?
  6.     203         cmp r1, r2          @ entry < bss_start ||. q$ t! O! d: N3 l: W
  7.     204         cmphs   r3, r1          @ _end < entry
    ) E8 u6 L0 T) [2 @/ _' k6 _; D
  8.     205         addlo   r1, r1, r0      @ table.  This fixes up the
    9 V# v$ ^3 y, o: w* G
  9.     206         str r1, [r6], #4        @ C references.* F3 g5 |. q( I1 n  G. F$ w
  10.     207         cmp r6, ip
    $ q* Y. o, V1 N+ v* H; o; l
  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。總之,可以看得出來我們將一些位址加上
" L+ o3 i+ M3 I' h5 x4 e了offset,很明顯的是因為我們載入的位置跟原本執行碼所預期的位址不同,因此必須做一些relocate的動作,若是不
# F7 ~: \3 E1 S) g" s3 J做的話,很可能程式碼會拿到不對的資料,我是jump到錯誤的地方執行。3 s+ u( N' ^4 x+ q& g. G
. l8 U- K( E- p8 K0 `
line 202~208, r1指向GOT table start,在沒有寫錯到bss區塊的情況下,將GOT裡面的資料都作relocate的動作。  P* i" R7 |' g2 p2 \- J
line 203, 204,應該是用來避免r1只到bss區塊。關於BSS也必須參考ELF format的東西, BSS是用來放置,未經初始2 Z7 g0 [) S. H# C
化的變數的地方。& \* R& T- t3 x8 {1 n3 l9 m

( g/ a3 k, S! w: r, l% r( C. k以上,我們發現kernel意識到自己被載入到某個地方,並且查看被載入到的地方是不是和compile9 ^8 E% @* j4 z0 h
time決定一樣,不一樣的話,自己手動修改一些需要做offset的資訊,等於是手動作relocate的事情。
作者: gogojesse    時間: 2008-8-14 07:02 PM
放了兩天假出去happy  
/ u2 L7 r% m% V接著繼續trace
  1.     211 not_relocated:  mov r0, #0
    $ J( U; l. [/ z9 Y+ m
  2.     212 1:      str r0, [r2], #4        @ clear bss" E" M  H7 k+ j9 G3 b- [% p
  3.     213         str r0, [r2], #4' }* Q+ y2 r# `" t" E
  4.     214         str r0, [r2], #4
    3 {2 @$ C- @0 o
  5.     215         str r0, [r2], #4
    ) O" ?7 u5 ]' {' [6 @
  6.     216         cmp r2, r3
    % x) G  j! O7 y9 f$ F3 q/ G
  7.     217         blo 1b' N8 J. G4 ?* T0 q% j
  8.     218
    ' y* D$ w8 u0 y3 M8 d" I1 U. {
  9.     224         bl  cache_on
複製代碼
恢復記憶一下,上次trace到kernel做了一些判斷,如果被載入的位址和compile time決定的位址不同,就會' Q6 A8 ~0 p- v  a) M, @5 D/ U
自己做relocate的動作,將一些ELF binary的特定pointer和value加上offset。那做完初步的relocate之後要做什麼?' @- K+ D& f7 X1 z

$ m) v8 W. P6 k& x4 P- E* z  Dline 212~215, 都是做store的動作,把r0存到r2所指到的位址,做完之後r2=r2+4。r2= bss start的位址.
) F* F$ j$ h) a換句話說,開始將bss裡頭的值都初始化成0。% c# p* F" ?: @- u3 J: u
lin3 216, 217, 確認一下是不是到了bss的底部,不到底部的話,jump到line 212繼續做搬移的動作。" z8 Y0 y! c3 X
7 H4 Y# k1 V& _5 j9 h
line 224, 做完bss初始化,jump cache_on
  1.     328 cache_on:   mov r3, #8          @ cache_on function
    + @" B6 C) N2 P  ]
  2.     329         b   call_cache_fn
    0 g/ t2 Z3 h: t* d  ]2 Q8 b
  3. # v+ U+ f' f3 y. C
  4.     537 call_cache_fn:  adr r12, proc_types
    + d0 K  U% \5 N
  5.     539         mrc p15, 0, r6, c0, c0  @ get processor ID( I' ~( o2 X% q2 P$ g

  6. 9 c- z! ^1 U6 p/ R
  7.     543 1:      ldr r1, [r12, #0]       @ get value
    ( M& Q9 u. k' r# M: q4 P9 p
  8.     544         ldr r2, [r12, #4]       @ get mask
    2 z! [- l, V. c' s
  9.     545         eor r1, r1, r6      @ (real ^ match)) m$ f4 h" X0 i5 I
  10.     546         tst r1, r2          @       & mask5 e0 Q' f  Z% T; X) D7 m
  11.     547         addeq   pc, r12, r3     @ call cache function9 n9 n7 ^( S8 y' ]
  12.     548         add r12, r12, #4*5
    / g. U4 C5 E. O, c4 {0 T. e9 q
  13.     549         b   1b
複製代碼
line 328, 將r3填入8, 不知道r3會拿做什麼用,繼續看。  p: M, j/ q- H2 f" L* |
line 329, jump到call_cache_fn。( T% O! [* N7 K: X7 c% ~
line 537, 將proc_types的位址讀到r12。
% m: u9 q. F. |" u3 _$ m9 _0 Oline 539, 將coprocessor裡頭的CPU id讀出來放到r6' D5 }- l0 h$ {4 w5 B9 w; L
line 543, 544, 將r12所指到的第一個位址的資料放到r1, offset 4bytes的資料放到r2,我們可以先觀( [% O7 W7 g6 f! @
察一下proc_types的長相(如下),註解上面寫了很多arm的家族的名稱,例如arm 6, armv5te等等,而且不' ^6 {& J2 L5 P! @% Z! s2 y  `
難發現都是先兩個.word,然後跟著三個『b xxxx_cache_xxx』,感覺很像是一組一組的資料。
" F. C# i0 a/ X5 ]line 545, 546, 將r6裡頭的CPU ID和讀出來的r1做exclusive OR,並且取mask,看看是否相等,相等的4 q- l4 M4 u/ I6 P) ^% |3 Y
話,就將pc設定r12+r3。換句話說,就是用CPU ID去確認值是否相等,值相等的話,就jump到r12+r3的位址。& J: M  l& q& P% c; h
line 548, 549, 不相等的話,就把r12加上5x4byte的offset跳回去繼續找。; c* e1 q: J5 E& r! i) n
整理一下,這邊的程式碼就是去proc_types的地方,比對CPU ID,比對成功的話,就呼叫該筆資料裡面的
3 F8 {: h. s) a+ F, M; Y2 ~cache function,至於呼叫第幾個function,就由r3控制,那所有CPU對應到的data structure就
' }( o7 s2 h: Q. E3 F$ l3 B  p從proc_types開始。2 ^) `: h& ^! M  ~6 r

0 q6 v/ U' U) d6 \0 B# T; H以ARMv5TE來說,r3=8,就剛好是cache_on的function。所以我們知道如果我自己發明了一個新的ARM CPU,也弄了一個新的id,這邊就需要修改出相對應的CPU的infomation,不然可能會找不到CPU ID。
  1.     566 proc_types:
    * K+ T8 }* a, v
  2.     567         .word   0x41560600      @ ARM6/610
    * ?$ x' F' B! e) B8 z( B, M
  3.     568         .word   0xffffffe0
    2 {0 T' X' ~( ~- |5 ]
  4.     569         b   __arm6_mmu_cache_off    @ works, but slow4 R, @2 z3 |2 D4 f
  5.     570         b   __arm6_mmu_cache_off
    ! N) h) ?* \  y
  6.     571         mov pc, lr" j' N: m/ J$ U( x
  7.     ......
    4 L) a4 D9 ]/ n2 H% T
  8.     640         .word   0x00050000      @ ARMv5TE- D$ ~1 F# A: u( Y, ~1 p* p
  9.     641         .word   0x000f0000' \/ M: S, S, e: }  o/ i6 ?
  10.     642         b   __armv4_mmu_cache_on- n( v% \) _, s4 U) ^2 V$ R
  11.     643         b   __armv4_mmu_cache_off, R* x9 H$ h" y
  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的一些流程.............感謝

  }8 X+ C# n( W; }3 v, Z# z6 q2 a. h- P  h9 H& L
謝謝   $ c" l# u" v) y# T, H
最近突然忙起來7 l0 p# g' P% _
改天有空再繼續study....
- L" e+ n1 f7 ], |7 Y1 N7 r; E4 ~9 E) i' _& D
另外,這篇是kernel booting的過程的程式碼,應該不能稱呼bootloader,不過) C/ f4 Y( C+ ]0 E+ g' S) T
有些概念跟bootloader差不多,可以幫助閱讀bootloader的code就是。  
作者: gogojesse    時間: 2008-10-7 12:43 PM
忙了好一陣子∼
  M( [; w7 t. k/ w- b5 }之前trace到 ./arch/arm/boot/compressed/head.S的 line 224 " {. z, [7 B" D* X' X1 {& D# P
呼叫了cache_on之後就沒寫了. v4 ?2 [# \% u3 e
現在接著開始( F' l3 Q9 `6 w; p# t9 X* Y
  r9 J: u& X$ C$ K3 d7 `
首先我們偷看一下code,
3 k# G$ D4 Q# c& c4 Cline 226, 將sp的值放到r1。5 y3 |- T8 B; R" [) _6 k% L
line 227 將sp的值加上0x10000放到sp。2 r" V! o8 W& u7 T7 G' T4 I2 S  d

% ?* R/ G5 s  u% S' C$ e, T為什麼kernel之前花了一些功夫將自己relocate到某個位置之後,要把cache打開,然後要開始對stack pointer(sp)做動作?目前還看不出來,所以接著trace下去。& h* X& U0 I# |0 s3 E4 ?: Y3 k4 k

8 E1 H% X/ _( f9 B" ^line 238,比較r4和r2的值,r4的值從line 158載入之後就一直沒被用到過,這個值是從一些makefile或是被makefile include進來的,然後在linking time的時候會被帶入,每個平台不一定一樣,通常你可到./arch/arm/mach-xxx/Makefile.boot去設定,這個值是用來指定kernel應該要被load到哪個位址上面執行。以omap1來說,9 V1 E5 V& c2 s0 X/ @
zreladdr-y       := 0x10008000
8 u6 r1 ?+ W4 c就是表示kernel會被載入到 0x10008000 的地方。這邊將r2和r4比較的用意是看看sp+0x10000之後會不會超過zreladdr的位置,應該是怕stack爆掉了會蓋到kernel的地方。(記住我們現在的kernel其實還在壓縮狀態,zreladdr是指解壓縮完要開始執行的狀態。)& ?* r. z  w5 y9 s/ n3 P8 a
3 W" E+ C( @9 u* V; y
line238~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 stack
    / a5 @! p0 s* [+ N5 t6 V$ n, b
  2.     227         add r2, sp, #0x10000    @ 64k max
    8 j- J) K7 u" q. u6 C. z
  3. 4 ^+ U, `4 {" v1 d3 f
  4.     238         cmp r4, r26 P# f/ H" Z$ M+ [6 b* S  c0 r
  5.     239         bhs wont_overwrite
    ' R5 X/ t1 I  v( n( k6 D9 w7 F
  6.     240         sub r3, sp, r5      @ > compressed kernel size
    1 z) C" S8 x, o9 U& H
  7.     241         add r0, r4, r3, lsl #2  @ allow for 4x expansion
    $ @; T' i: R( B% t
  8.     242         cmp r0, r5
    ; K3 l- b/ {4 X+ `2 e9 _' U
  9.     243         bls wont_overwrite% y5 r( n, s' P4 K# p5 y9 ?  {2 i
  10.     244$ E" V; Z  Z: i! k: `6 N/ [$ @0 [
  11.     245         mov r5, r2          @ decompress after malloc space# Q* N; k7 f* w( r$ d
  12.     246         mov r0, r5
    # E) A+ {) b2 O6 G
  13.     247         mov r3, r72 |; B0 F, [$ E
  14.     248         bl  decompress_kernel
    $ A( D0 j" e, m  T
  15.     249
    + q# D1 H6 J; O- H. X: L
  16.     250         add r0, r0, #127 + 128  @ alignment + stack
      l1 X; {4 C6 H9 Y
  17.     251         bic r0, r0, #127        @ align the kernel length
複製代碼
跳到wont_overwrite之後,當然就是要開始把kernel解壓縮,
! Y, q# I6 J' `' Qline 283,把r4搬到r0,r4就是我們剛剛說的kernel被解壓縮之後的位址。(也就是解完之後應該要執行的位置)& k4 h' P$ O- @- R) R0 W
line 284,把r7搬到r3,r7從一開始讀進來之後,也沒用過,理論上是architecture ID。
4 g, R0 A- B# x) Y7 V: S  B* q7 Mline 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
    4 G5 i2 H' e4 L) u, \
  2.     284         mov r3, r7% G6 y! x" X0 `9 U$ J
  3.     285         bl  decompress_kernel
    / c9 @: n8 f5 W) @
  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
由於解壓縮不是我們的重點
2 t2 ~: R7 y" `沒有trace
- O! g% T& ?2 X. s7 E4 Z: }! ~假設一切都順利
' h1 \& G# f$ S( ndecompress_kernel結束後
( ~* E% Q, W  [$ |6 k; S3 ?, M我們就得到一個解壓縮完的kernel放在r4指向的位置, U/ `+ W  M' |/ O% Y" z$ @  T
line 286,會jump到call_kernel,如下:
3 a. f9 V/ C0 l) d& u5 ^1 Bline 516, flush cache
: p# \- U+ ?! @1 I2 s; U) r4 r8 G/ uline 517, 關掉 cache$ ]- T3 `) u4 B( r: O
line 518~520,將r0, r1, r2分別填值。
8 Q" E* C: D( e+ jline 521,將program counter指到r4,也就是解壓縮的kernel的一開頭。
, @1 z8 K  ]1 G9 W% t. R6 p/ u
/ n" i: _# u: F2 Z到這邊我們終於結束head.S的工作,解壓縮並且跳到另外一個object code的開始。跳到解壓縮的開始位置,究竟會進入哪一個function?
  1.     516 call_kernel:    bl  cache_clean_flush
    ' g* Z8 G  G# s  {0 T
  2.     517         bl  cache_off
    . @) {, X8 [" ^
  3.     518         mov r0, #0          @ must be zero
    ! Q$ W# a0 ?3 a8 u! _7 p  y
  4.     519         mov r1, r7          @ restore architecture number
    7 g7 k  S+ c" q
  5.     520         mov r2, r8          @ restore atags pointer
    ) \1 w+ E! ~8 C3 I0 n' Q1 ~
  6.     521         mov pc, r4          @ call kernel
複製代碼

作者: kkbbs    時間: 2008-10-11 10:39 PM
很棒的分析....
; `5 q! \9 ]- f) Y( @. F. x$ u非常據有參考價值; k/ d4 @; f3 S; ^  n: d" _
感謝大大分享    感恩
作者: gogojesse    時間: 2008-10-13 10:15 AM
原帖由 kkbbs 於 2008-10-11 10:39 PM 發表
' A; r% a7 R. F* n$ w( Z- [- t$ q很棒的分析....: G- e! U* a3 a) V8 C
非常據有參考價值
% i! J9 M7 N: D0 Q# T7 K' Q# c感謝大大分享    感恩
6 R7 q% k% Y6 j3 y
* g5 [, x% R& d+ C. C
謝謝   3 I$ R% D% y1 E! S- f
有哪邊寫錯或是有怪怪的地方( y, ?) i- O9 Y/ i" A; }" B
歡迎提出來一起想想...




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