Chip123 科技應用創新平台

 找回密碼
 申請會員

QQ登錄

只需一步,快速開始

Login

用FB帳號登入

搜索
1 2 3 4
查看: 7353|回復: 17
打印 上一主題 下一主題

trace linux kernel source - ARM - 03

[複製鏈接]
跳轉到指定樓層
1#
發表於 2008-10-7 14:00:18 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
到目前為止,我們已經進展到kernel幫自己relocate完,並解將自己解壓縮到一個地方要準備開始執行,那疑問來了?到底是跳到哪裡去了,因為./compressed/head.S最後一行居然是( S! B" k  h2 F5 V1 C- v
『mov pc, r4』
& ]& Z' q( [/ g" p: P; S; xr4只代表了解壓縮完後kernel的位址,那究竟整包kernel編譯的時候,哪個function哪個東西被放在最前面咧?!; a' n3 d( _9 u6 p. ~3 A
& Y& B- V0 b6 ?+ x: e
所以我們又必須開始找於kernel link的時候是怎麼被安排的,有了前面的基礎,我們可以從Makefile知道程式碼如何被編譯。至於link上的細節,例如有那些section和section先後順序等等,可以從 lds 檔來規範。3 l: W; n" s3 \3 k$ D% x
! Q: h0 H( {3 \
有興趣的人可以看一下 kernel source 根目錄裡頭的 Makefile,Makefile file裡面指定了使用vmlinux.lds來當做lds檔。
  1. 659 vmlinux-lds  := arch/$(SRCARCH)/kernel/vmlinux.lds
複製代碼
打開./arch/arm/kernel/vmlinux.lds.S (會用來產生vmlinux.lds)$ \1 w( N# `( ?
我們可以發現第一個section是『.text.head』,裡頭的_stext從目前的位置開始放。
  g# M3 i0 L! ^1 r# B. p於是我們曉得只要找到屬於.text.head這個section,並且是_stext這個symbol的程式碼,就是解壓縮完後的第一行程式碼。
  1.      26     .text.head : {
    , s9 @' R& s0 b. D
  2.      27         _stext = .;3 K( j( o: [7 H9 I8 Q9 g: _
  3.      28         _sinittext = .;
    / P3 `* x" n% y7 w% }7 g
  4.      29         *(.text.head)4 l9 l( m" s5 z# I
  5.      30     }
複製代碼
用指令搜尋一下,發現 ./arch/arm/kernel/head.S 裡頭有關鍵字(如下),結果我們從./arch/arm/boot/compressed/head.S跳到了./arch/arm/kernel/head.S
  1.      77     .section ".text.head", "ax"7 u. D7 [+ M; b
  2.      78     .type   stext, %function
    $ _1 h1 l5 o: d5 b* r9 V
  3.      79 ENTRY(stext)
    " N1 _2 A& D' P
  4.      80     msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode& b/ L! j+ Q* l  L  t5 y) |. v2 A
  5.      81                         @ and irqs disabled! r; ?* D+ k6 ]0 D5 q+ O, `8 L
  6.      82     mrc p15, 0, r9, c0, c0      @ get processor id
    8 j) p* h4 D7 t* Z6 p5 [
  7.      83     bl  __lookup_processor_type     @ r5=procinfo r9=cpuid8 ]5 y" `3 N$ E! {& R
  8.      84     movs    r10, r5             @ invalid processor (r5=0)?
    # r: a3 t+ Q: r) p$ K
  9.      85     beq __error_p           @ yes, error 'p'0 g7 D, c# a0 o
  10.      86     bl  __lookup_machine_type       @ r5=machinfo
    " E' ]" ^, T) q2 [
  11.      87     movs    r8, r5              @ invalid machine (r5=0)?
    / ^2 c  q8 f9 Z
  12.      88     beq __error_a           @ yes, error 'a'
    6 [2 X  V" K$ H4 v/ `7 B
  13.      89     bl  __vet_atags
    ) U: I0 }: M- M7 ~
  14.      90     bl  __create_page_tables
複製代碼
既然找到了檔案,我們又可以開始繼續。5 d; y; D- x, O. P
7 C- s* Y- U& s7 C/ Y5 J6 Q
看了一下,程式碼多了很多bl的跳躍動作,看來會在各個function跳來跳去。
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享分享 頂 踩 分享分享
2#
 樓主| 發表於 2008-10-9 15:32:16 | 只看該作者
既然跳到真正的kernel開始跑,表示進入重頭戲,在進入kernel之前arm的平台有一些預設的狀況,也就是說arm kernel image會預設目前的cpu和系統的狀況是在某個狀態,這樣對一個剛要跑起來的OS比較決定目前要怎麼boot起來。3 n. [2 m, _5 H2 A

& A8 x  Z1 R0 q& H7 l9 s可以看一下comment,有清楚的描述。這也幫助我們了解為什麼decompresser的程式跑完之後,還要把cache關掉。
  1.      59 /*) K" z1 J* j0 s9 E2 @
  2.      60  * Kernel startup entry point.1 Z( O( E/ y' V: U. |" Q: ?
  3.      61  * ---------------------------
    4 F% d! j5 P% Q1 W1 ~- d
  4.      62  *
    " b: S7 P# J, F. G
  5.      63  * This is normally called from the decompressor code.  The requirements
    0 N2 W2 J; c0 \( T, e, ]# C3 ~' Y* O
  6.      64  * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
    ! @! u2 D0 L3 d2 H5 I4 z" ?6 {0 X
  7.      65  * r1 = machine nr, r2 = atags pointer.
複製代碼
基於以上的假設,當program counter指到kernel的程式的時候,就會從line 80開始跑。
0 `# @8 Z. o8 mline 80, msr指令會把, operand的值搬到cpsr_c裡面。這是用來確保arm cpu目前是跑在svc mode, irq&fiq都disable。(設成0x1就會disable,definition在./include/asm-arm/ptrace.h)
5 W4 a& y8 U  P5 o" |- Lline 82, 讀取CPU ID到r9
2 a4 ~# J( z1 Z5 f5 x2 Cline 83, 跳到 __lookup_processor_type 執行(bl會記住返回位址)。
  1.      77     .section ".text.head", "ax"# n6 n- z# r' \# F  s
  2.      78     .type   stext, %function
    # V) o8 l' c% F4 \
  3.      79 ENTRY(stext)2 b1 G/ \/ D- E' P( i% w% t0 U. T
  4.      80     msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
    . ~+ D) x: V3 `( r
  5.      81                         @ and irqs disabled6 Y+ Q/ O! _6 f9 j' O
  6.      82     mrc p15, 0, r9, c0, c0      @ get processor id& ^! i9 g) s7 \, D# o  `: p
  7.      83     bl  __lookup_processor_type     @ r5=procinfo r9=cpuid
複製代碼
接著會跳到head-common.S這個檔,
: X& b0 ~& L: O6 Z" Z: uline 158, (3f表示forware往前找叫做『3』label)將label 3的address放到r3。2 d4 m( E3 D4 r, E2 n' G
line 159, 將r3指到的位址的資料,依序放到r7, r6, r5.(ldmda的da是要每次都位址減一次)
5 O* _- Q. @6 P) {" O( _2 vline l60, 161, 利用真實得到位址r3減去取得資料的位址得到一個offset值,這樣可計算出r5, r6真正應該要指到的地方。: J2 `# {: ^4 w0 u* i
line 163~169,這邊的程式應該不陌生,就是在各個CPU info裡面找尋對應的structure。找到的話就跳到line 171,返回head.S
! K, n9 o- R" R1 t) y% I0 p; pline 170, 找不到的話,r5的processor id就放0x0.表示unknown id。, U/ Q! m, q5 Z: f2 v; Y' u5 a& Q& M

0 y6 s2 K, |( U  }0 g" H8 I__proc_info_xxx可以在 vmlinux.lds.S 找到,是用來包住CPU info的所有data.資料則是被定義在./arch/arm/mm/proc-xxx.S,例如arm926就有 proc-arm926.S,裡面有相對應的data宣告,compiling time的時候,這些資料會被編譯到這個區段當中。
  1.     156     .type   __lookup_processor_type, %function
    $ v& j6 s  r( Y1 }6 R
  2.     157 __lookup_processor_type:
    3 s* K! g1 G" a- l% M2 }
  3.     158     adr r3, 3f
    9 I1 k$ s/ I1 o" l
  4.     159     ldmda   r3, {r5 - r7}3 W6 g  T8 Z* r/ f6 y
  5.     160     sub r3, r3, r7          @ get offset between virt&phys
    ' W, c4 C& Z2 x  o
  6.     161     add r5, r5, r3          @ convert virt addresses to, D) P! l3 S! z7 S8 e6 ?, y
  7.     162     add r6, r6, r3          @ physical address space
    4 n; A$ n2 t6 v
  8.     163 1:  ldmia   r5, {r3, r4}            @ value, mask
    * s% L* @5 k# O% Q- @7 Y
  9.     164     and r4, r4, r9          @ mask wanted bits1 ~$ B' D% B3 j5 P
  10.     165     teq r3, r4
    # b/ D. L5 i# u  l
  11.     166     beq 2f, q7 A+ P$ j) E7 C% n" k) X* J
  12.     167     add r5, r5, #PROC_INFO_SZ       @ sizeof(proc_info_list)- S5 A5 h9 E3 e! D: U% c
  13.     168     cmp r5, r62 k, y! J4 a" S2 `: m
  14.     169     blo 1b
    ) t/ `# K' z8 s# s
  15.     170     mov r5, #0              @ unknown processor7 N" R* W& |5 J
  16.     171 2:  mov pc, lr
    4 W8 a: F6 i/ P1 K- k
  17. 1 l+ o/ B- F$ _7 D: I; ^
  18.     187     .long   __proc_info_begin
    ' l% R( f# t' M& m) u; h
  19.     188     .long   __proc_info_end
    % U. `& j$ T! m% x& M. l& Y
  20.     189 3:  .long   .$ v# R8 V+ {& u) o8 D6 f
  21.     190     .long   __arch_info_begin4 q+ {9 V& d- v  a7 z0 h0 Y1 L9 h
  22.     191     .long   __arch_info_end
複製代碼
跳了很多檔案,建議指令和object code如何link的概念要有,就會很清楚了。
3#
 樓主| 發表於 2008-10-13 17:20:35 | 只看該作者
我們從 head-common.S返回之後,接著繼續看。
1 w# d( I& Q8 H: A7 S, E  {) l2 w& i# \( C# L
) e# u0 V+ v7 v+ D: C& Sline 84, movs的意思是說,做mov的動作,並且將指令的S bit設起來,最後的結果也會update CPSR。這個指令執行的過程當中,會根據你要搬動的值去把N and Z flag設好。這個是有助於下個指令做check的動作。
9 K& E+ Y. i3 [6 l. Wline 85, 就是r5 = 0的話,就跳到__error_p去執行。
1 I' S( t9 P( J8 k. }( b; ?line 86, 跳到__lookup_machine_type。原理跟剛剛找proc的資料一樣。
  1.      83         bl      __lookup_processor_type         @ r5=procinfo r9=cpuid
    + [) o! T1 G: Q- U8 `% x
  2.      84         movs    r10, r5                         @ invalid processor (r5=0)?
    5 i/ R6 [9 T" R$ A
  3.      85         beq     __error_p                       @ yes, error 'p'1 Y6 f" I" H& P
  4.      86         bl      __lookup_machine_type           @ r5=machinfo
複製代碼
看得出來跟proc很像,有個兩個小地方是
" n5 `$ i/ \7 P, e) Y
+ Q( W9 I+ o) ]8 I1. line 207,用ldmia不是用ldmda。原因就在於存放arch 和 proc info 的位址剛好相反。一個在lable3的上面。一個在的下方。設計上應該是可以做修改的。
: f. {$ _4 T: e( ?+ N! _' k& T/ ?, i( q3 R2 F& ]3 c: @
2. arch定義的方式是透過macro,可以先看 ./include/asm-arm/mach/arch.h。裡頭有個 MACHINE_START ,這邊會設好macro,到時候直接使用就可以把arch的info宣告好。 例如 ./arch/arm/mach-omap1/board-generic.c
  1. /* macro */
    ) s; x, t5 V% K6 W/ F$ c4 p" A
  2.      50 #define MACHINE_START(_type,_name)                      \
    ( R/ F$ R, V: i: u
  3.      51 static const struct machine_desc __mach_desc_##_type    \
    6 D% V0 ~" j& @9 E6 H, {, n4 C+ ?
  4.      52  __used                                                 \
    8 N& d" u* e1 T* V
  5.      53  __attribute__((__section__(".arch.info.init"))) = {    \) D: O" N, w" k' D6 M
  6.      54         .nr             = MACH_TYPE_##_type,            \
    5 n  W- P6 m, K( N  D, R
  7.      55         .name           = _name,$ U/ r, ^" P* f+ N/ C* D
  8.      56
    , ?# o$ u$ s4 W1 `
  9.      57 #define MACHINE_END                             \
      A& k0 o8 |6 N) |
  10.      58 };7 Z3 D$ t2 g; X3 e* V- g, k* ?
  11.      /* 用法 */
    " \8 c1 u. S2 I7 J2 M
  12.      93 MACHINE_START(OMAP_GENERIC, "Generic OMAP1510/1610/1710")- X* ?* x" G: i/ g
  13.      94         /* Maintainer: Tony Lindgren <tony@atomide.com> */
    : k- q9 w6 t" h! O1 a
  14.      95         .phys_io        = 0xfff00000,$ c, y; z/ M# i4 O6 z7 P2 v4 @
  15.      96         .io_pg_offst    = ((0xfef00000) >> 18) & 0xfffc,
    & S! f9 K5 G2 L* j. }/ N
  16.      97         .boot_params    = 0x10000100,% T9 u! p* E! y! v! T+ {4 w/ D
  17.      98         .map_io         = omap_generic_map_io,
    : T0 R/ j9 t( u
  18.      99         .init_irq       = omap_generic_init_irq,
    6 v* K$ A2 M6 [' f- `; S0 f
  19.     100         .init_machine   = omap_generic_init,
    ' M3 V% ^% U' X' T5 y' o
  20.     101         .timer          = &omap_timer,
    7 v, D7 d/ G9 H4 ]. n% S
  21.     102 MACHINE_END
    6 }8 R& z- l" I1 q4 r: \# ?
  22. : Q9 K  i: N8 ~! U4 y
  23.     /* func */
    3 I3 p7 `* d& p% R+ R2 B
  24.     204         .type   __lookup_machine_type, %function
    9 B! e0 H  ^* Q& ^9 |' G& _/ M' P
  25.     205 __lookup_machine_type:& C: H+ G4 \( d) ]* D' P
  26.     206         adr     r3, 3b' V8 r  M3 k# _- p1 T# H9 ]& K
  27.     207         ldmia   r3, {r4, r5, r6}& ]) K7 p3 a# y6 X6 C
  28.     208         sub     r3, r3, r4                      @ get offset between virt&phys
    & e) [" L. M& Q. @2 }1 A4 F' \
  29.     209         add     r5, r5, r3                      @ convert virt addresses to
    ! a* O; R& l2 c& x6 `
  30.     210         add     r6, r6, r3                      @ physical address space2 g4 l5 b: w& g* Y/ g' I
  31.     211 1:      ldr     r3, [r5, #MACHINFO_TYPE]        @ get machine type+ e! G; `7 ~( }: c" A' ~4 f
  32.     212         teq     r3, r1                          @ matches loader number?0 s! Q) N* p; l$ r9 ]. e! s
  33.     213         beq     2f                              @ found
    - m3 l+ Y8 d; P* G0 l5 R# `
  34.     214         add     r5, r5, #SIZEOF_MACHINE_DESC    @ next machine_desc
    # D' K6 z4 q( v3 ^% `( l2 C
  35.     215         cmp     r5, r6
    + m- A3 a: m; u6 E/ D$ r
  36.     216         blo     1b; R, @9 {7 s  r5 U5 _
  37.     217         mov     r5, #0                          @ unknown machine
      d) F# i# r# ~9 v6 g5 g
  38.     218 2:      mov     pc, lr
複製代碼
4#
 樓主| 發表於 2008-10-13 17:56:46 | 只看該作者
接著我們又返回到head.S,5 A4 o/ _( p: V
line 87~88也是做check動作。3 R' w3 q( t$ L; o+ b) @
line 89跳到vet_atags。在head-common.S
  1.      87         movs    r8, r5                          @ invalid machine (r5=0)?" f+ U' F$ Z( D3 k/ E
  2.      88         beq     __error_a                       @ yes, error 'a'
    6 C) y9 D2 Z# D3 w3 ?+ |, [3 m- M5 ]9 ^, l
  3.      89         bl      __vet_atags
    3 [# ?: |, f' \7 j6 z
  4.      90         bl      __create_page_tables
複製代碼
line 245, tst會去做and動作。並且update flags。這邊的用意是判斷位址是不是aligned。& O6 H$ C% c7 s; s( ^+ @. D3 U; d
line 246, 沒有aligned跳到label 1,就返回了。
2 ], P* s. n4 g; R5 o0 D$ Hline 248~250, 讀取atags所在的address裡頭的值到r5,看看是否不等於ATAG_CORE_SIZE,不等於的話也是返回。: l) w, y; g1 d0 P4 v
line 251~254, 判斷一下第一個達到的atag pointer是不是等於ATAG_CORE。如果正確的話,等一下要讀取atag的資料,才會正確。
- y/ O# o# S4 E4 K(atag是由bootloader帶給linux kernel的東西,用來告知booting所需要知道的參數。例如螢幕寬度,記憶體大小等等)
  1.      14 #define ATAG_CORE 0x54410001  c5 H) t* T9 F% {( f  Q) _" q- C
  2.      15 #define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
    - M1 N( g0 D$ K$ A) P" s

  3. 1 Y0 l; `  g' s, X3 O
  4.     243         .type   __vet_atags, %function# b% U3 V7 _% A
  5.     244 __vet_atags:
    3 B( [. Z8 K& \) |' A: F& M# \
  6.     245         tst     r2, #0x3                        @ aligned?
    , Z: {8 w6 |8 z, K% E6 ~7 y. P5 C1 E
  7.     246         bne     1f) C. u: V: a# j
  8.     247! E9 m7 S1 ^6 U& f: P6 T" i/ ]8 g
  9.     248         ldr     r5, [r2, #0]                    @ is first tag ATAG_CORE?
    & ^! M! r5 E* p, I  E7 L
  10.     249         subs    r5, r5, #ATAG_CORE_SIZE( @$ i3 n. y0 t' q! O7 U9 }
  11.     250         bne     1f" p5 H& I/ {3 l- O2 \1 W" Q
  12.     251         ldr     r5, [r2, #4]4 G/ ]8 {2 m+ Y! }
  13.     252         ldr     r6, =ATAG_CORE
    : s& {) G( A+ C  G
  14.     253         cmp     r5, r60 `' O. _3 v8 E
  15.     254         bne     1f3 N- b% v" I. |6 Z& _& E
  16.     255
    , C" L$ W+ g/ m7 I3 T7 Q2 m
  17.     256         mov     pc, lr                          @ atag pointer is ok
    : i% Z9 c1 |" L: s; v
  18.     257; s! w7 A8 D# e; r4 u  R' b
  19.     258 1:      mov     r2, #0
    & q4 N5 O& R: T# j0 V4 ~% s1 I0 w
  20.     259         mov     pc, lr
複製代碼
接著我們又跳回去head.S。  
  `1 A4 |7 R0 s; e2 _line 90,又跳到 __create_page_tables。   (很累人....應該會死不少腦細胞)
% l. K5 l* g6 p8 s哇!page table?!!如雷貫耳的東西,不知道會怎麼做。剛剛偷看了一下,@@還蠻長了,先到這邊好了,下次在繼續寫。
5#
 樓主| 發表於 2008-10-14 12:13:51 | 只看該作者
由於code看起來似乎越來越難解釋,會需要在檔案之間跳來跳去。建議一些基礎的東西可以再多複習幾次(其實是在說我自己 ),閱讀source code上收穫會比較多。例如:% t" J) w( T0 D) V: m

; ^, `- m; w: z2 i0 m1. arm instruction set - 這個最好每個指令的意思都大略看過一次,行有餘力多看幾個版本,armv4, v5 or v6。
) V. V  A* ]( R9 A  A2 p2. compiler & assembler & linker - toolchain工具做的事情和功能,大致的流程和功能要有概念。
$ J( J" k$ v: S/ L4 L5 D# e3. Makefile & link script - 這兩個功能和撰寫的方式要有簡單的概念。! I. J2 u+ D  w- r: X, G
4 a& }! Z, n+ h
以上1是非常重要的重點,2&3只要有大致上的概念就可以,因為trace code的時候,有時需要跳到Makeflie&Link script去看最後object code編排的位址。5 |; j* R' i# y' J6 c% m

6 L$ l! M# I& ?! W. K! R7 r* t' X3 X由於我們trace到了page table這個關鍵字,在開始之前,稍微簡短的解釋,為了幫助了解,儘量用易懂的概念講,有些用詞可能會和一些真實狀況不同,但是懂了之後,應該會有能力分辨,至於正式而學術上解說,很多書上應該都有,或是google一下就很多啦∼
6#
 樓主| 發表於 2008-10-14 12:14:47 | 只看該作者
page table本身是很抽象的東西,尤其是對所謂的 user-mode application programmer 來說,大部分的狀況它是不需要被考慮到的部份,但是他的產生卻對os和driver帶來了很多影響。我們從一個小小的疑問出發。
6 W" j$ g4 ~$ v! _3 C2 C. i, J4 o
6 R" C6 j4 }# \1 ?" e2 m# k『產生page table到底是要給誰用的?』$ e% E$ H' _, j  W' T% q* l

# N9 L" S' I6 ]5 Z) `- j其實真正作用在它上面的H/W是MMU,一旦CPU啟用了MMU,當cpu嘗試去記憶體讀取一個operand的時候,cpu內部打出去的位址訊號都會先送到mmu,mmu會拿著這個位址去對照page table,看看這個位址是不是被轉換到另外一個位置(還會確認讀寫權力),最後才會到真正的位址去讀寫。) [  u$ V7 H8 d

/ U# j3 o2 V; M這樣來看CPU其實一開始打出去的位址訊號其實不是最後可以拿到資料的位址,我們稱為virtual address(va),mmu打出來的位址才能真正拿到資料,所以我們把mmu打出去的位址稱為physical address(pa)。那用來查詢這個位址對照關係的表格,就是page table。
$ {1 I0 S, G+ f% u& q! Q% ~. }( g* [' @- s$ k4 W
到這邊我們有一個簡單的概念。來想像一個小問題,一個普通的周邊(例如你的顯示卡),因為他並不具有MMU功能,hw只看得懂pa,但是CPU卻是作用在va,假如我想去對hw的控制暫存器做讀寫,到底要用va還是pa?
7#
 樓主| 發表於 2008-10-14 12:15:51 | 只看該作者
這時,寫driver的人就必須要小心,設定給硬體看的位址必須要先轉成pa(像是設定DMA),給CPU執行的程式碼要使用va,這對一開始嘗試寫driver但是又不瞭解va pa之間的差別的人,常常產生很多疑問。
: [  c* {* Q5 j$ S6 Y' k( V( Z# I
8 I; g% u9 W" o& e0 \/ [現在我們回頭想想OS,既然我們跑到create page table,可以預期的是他想要將MMU打開,因此希望預先建立起一個page table,讓MMU知道目前os想規劃的位址對應和讀取權力是如何被安排的。所以os必須考慮到現在的系統究竟是長怎樣?應該要如何被安排?是不是有那些要被保護?好吧∼因為我們完全對os不了解,也不知道該安排什麼,只能祈禱在trace code完後,可以找到這些問題的答案,或者發現一些沒想到的問題。! G5 b4 M! n% b1 b% k* G1 S% N
9 G8 c: [, H8 b1 x
知道了page table的大致上的功能,下篇就可以專心的研究這個table的長相,和它想規劃出的系統模樣。
* o, {5 p/ [- X3 Y, U3 d6 c: O4 `! R& X- Y9 |. p7 ?, d; d6 a$ o
p.s. 字數限制好像變短了。   (看來很難寫)
8#
 樓主| 發表於 2008-10-14 13:56:17 | 只看該作者
由於字數限制挑整,用詞儘量精簡,以便可以貼必要source。另外,以後寫到一段落,考慮收集成一篇完整的文章,這樣應就不會因為用回覆的方式,造成閱讀斷斷續續的問題。希望有人對文章呈現方式有想法的話,可以跟我講。
9#
 樓主| 發表於 2008-10-14 13:57:39 | 只看該作者
現在,讓我們跳入create_page_tables吧∼
. K3 S, F4 n$ @4 c( |7 M6 pline 216,會跳到pgtbl的macro去執行,其實只是載入一個位址。6 s+ z1 f  d3 o3 g: e

, d( R) L7 s. K3 X9 O/ [0 i+ C: O. U8 g9 A只是這個位址因為你硬體規劃dram位置不同,所以必須可以變動。一般會定義在./include/asm-arm/arch-你的平台/memory.h,我們看得出來dram開始的地方是從0x8000 offset(text_offset)開始算,猜測可能一開始有保留空間給kernel使用。實際算page table的時候有減去0x4000,表示是從DRAM+0x8000-0x4000開始放pg table.
  1. /* arch/arm/Makefile */& U' H0 ]- m  B, F# k/ m
  2.      95 textofs-y       := 0x00008000+ B- y2 f( H* {/ \! N, P) Z" E( }2 o
  3.     152 TEXT_OFFSET := $(textofs-y)
複製代碼
  1. /* include/asm-arm/arch-omap/memory.h */2 h5 R# f9 l. |3 e
  2.      40 #define PHYS_OFFSET             UL(0x10000000)7 {2 j2 @, H$ t# ?! @
  3. + h/ I5 c5 Y5 `, K6 ~( M1 s
  4.      /* arch/arm/kernel/head.S */
    - @7 m0 w" Z1 E, \
  5.      29 #define KERNEL_RAM_VADDR        (PAGE_OFFSET + TEXT_OFFSET)  `& R" W+ r, o6 J1 h5 i# J7 ^
  6.      30 #define KERNEL_RAM_PADDR        (PHYS_OFFSET + TEXT_OFFSET)
    # H) k: A) w* @$ P& n1 S
  7. ( z) E2 W  g# C: c
  8.      47         .macro  pgtbl, rd
    # T9 u" K, }* H5 Z/ M3 m) y& L
  9.      48         ldr     \rd, =(KERNEL_RAM_PADDR - 0x4000)2 W2 I8 K0 c( g% f5 E" r! n7 d( U
  10.      49         .endm
    ! R6 W( l4 F2 r' e1 R3 N4 s
  11. ( w# j' K+ R+ h( R4 u
  12.     216         pgtbl   r4                              @ page table address
複製代碼
10#
 樓主| 發表於 2008-10-14 14:16:53 | 只看該作者
得到pg table的開始位置之後,當然就是初始化囉。
) Q9 O2 k3 f# ]line 221, 將pg table的base addr放到r0.
+ {0 t9 a" Q9 O& G0 Z" Oline 223, 將pg table的end addr放到r6.) m  z$ F3 P0 w' }' m
line 224~228, 反覆地將0x0寫到pg table的區段裡頭,每個loop寫16bytes. (4x4),直到碰到end,結果就是把它全部clear成0x0.
  1.     221         mov     r0, r4
    7 u6 E7 I5 o, x% W8 r$ a* U+ j$ Y
  2.     222         mov     r3, #0; U( q& z( B8 ~& k7 k
  3.     223         add     r6, r0, #0x4000
    / ]9 J) C6 F4 ~5 o  A
  4.     224 1:      str     r3, [r0], #4+ l7 B  r( t6 H6 l+ y
  5.     225         str     r3, [r0], #4
    7 j# Y5 K1 z' u2 [+ l( |
  6.     226         str     r3, [r0], #4
    + C; |& p* t  R% z
  7.     227         str     r3, [r0], #4
    0 G) w9 C; [' E8 f' ~5 B9 k
  8.     228         teq     r0, r6
    - e, o$ ^  U$ K  y8 l6 @
  9.     229         bne     1b
複製代碼
line 231, 將位址等於 r10+PROCINFO_MM_MMUFLAGS 裡頭的值放到r7。r10是proc_info的位址。proc的info data structure被定義在『./include/asm-arm/procinfo.h』,offset取得的方式用compiler的功能,以便以後新增structure的欄位的時候不需要更動程式碼。這邊的動作合起來就是讀預設要設給mmu flags的值。
  1.    231         ldr     r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
複製代碼
11#
 樓主| 發表於 2008-10-14 15:11:48 | 只看該作者
問題怎麼填值??- h4 k9 |) ~7 w& k2 W) m7 `8 N
拿出ARM的手冊,翻到MMU章節。一看發現page有很多種,還得分first level和second level。
- j; Z9 @3 T3 p* ~% u2 ~
* e: w; }# o4 t0 V2 B念書時的印象,是從1st level在去查2nd level,先看怎麼設定1st level (不知以前老師有沒有亂教)
, o0 V# x2 t/ v& f( S5 k1. [31:20]存著section base addr5 H1 Y! X9 ]/ ?7 d
2. [19:2]存著mmu flags
5 S0 b* M) c8 G: n' G" O3. [1:0]用來辨別這是存放哪種page, 有四種:5 v  N7 j2 C2 o, {# [
   a. fault (00) b. coarse page (01) c. section (1st level) (10) d. fine page (11)
+ g* Y" u/ D* {3 Z% a4. pg tabel資料要存放到 [31:14] = translation base, [13:2] = table index , [1:0] = 0b00 的位址
! a' F: e* f- n+ n4 _+ l6 Y) c4 D$ b/ n1 Y. v
來看code是怎麼設定。
1 q' A( J, V1 v0 d' T( y5 r6 `  f) X' \- e
line 239, 將pc的值往右shift 20次放到r6.這是取得前20高位元的資料,有點像是 1.。/ v4 v) K7 c1 m& l) C: t# P
line 240, 將r6往左shift 20次之後,和r7做or的動作,剛好也是 2.提到的mmu flags和1.處理好的資料做整理。
/ L) D, Z3 y; N* G所以前面兩個做完,就完成了bit[31:2]。0 G! ]/ w" T" {. e# s2 {
line 241, 將r3的資料寫到r4+(r6<<0x2)的地方去,剛好是4.提到的位址。(lucky)
  1.     239         mov     r6, pc, lsr #201 R  D& \( ~$ W3 D+ a2 l
  2.     240         orr     r3, r7, r6, lsl #20
    / k+ v5 z! U# H; b) R
  3.     241         str     r3, [r4, r6, lsl #2]
複製代碼
p.s. 用pc值剛好可以算出當前的page。
12#
 樓主| 發表於 2008-10-22 19:47:03 | 只看該作者
最近又被釘上了,開始忙碌,大概沒太多時間貼文∼" p; z- C3 E" e6 m! A8 r! s

8 I3 _; s1 t" T; i9 ~上篇已經將pc所屬的page table entry(pte)設定好,接著繼續看
6 I; T# I& B" B5 dline 247, 248, 將KERNEL_START的位址往右shift 18算出pte的offset,(等於line239&241,239&241是先shift right 20然後shift left 2),並將剛剛r3的值設給pte。『!』的意思是會把address存到r0。
+ o* Q: Z6 W1 J" Q" C* J0 L6 h' ~6 [) Q5 }! u+ n
line 249~252, 算出KERNEL_END-1的pte位址放到r6, KERNEL_START的下一個pte的位址放到r0。r0 <= r6的話就持續對pte寫入初值的動作。但是這邊的r3有加上(0x1<<20),所以原本的section base會變成加1,目前不是很明瞭為什麼要加1,或許往後面會找到答案。
  1.     247         add     r0, r4,  #(KERNEL_START & 0xff000000) >> 185 L* X0 Q0 t+ g! ^8 B7 K7 ]" O
  2.     248         str     r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]! + @% Q5 s0 o: ]1 ]$ Z3 y
  3.     249         ldr     r6, =(KERNEL_END - 1)5 h2 m3 N5 E; v" ]" M
  4.     250         add     r0, r0, #45 G) n, D- [7 V1 x! p$ E
  5.     251         add     r6, r4, r6, lsr #18
    0 G  a+ c& a  d
  6.     252 1:      cmp     r0, r6
    # {$ M$ }7 G' j! Q  g; D2 D! f
  7.     253         add     r3, r3, #1 << 20$ R4 ]6 o+ j  L* s5 m! |
  8.     254         strls   r3, [r0], #4
    5 o, U" Z; m. e& C
  9.     255         bls     1b
複製代碼
13#
 樓主| 發表於 2008-10-22 20:24:58 | 只看該作者
line279,PAGE_OFFSET是規範RAM mapping完後的virtual address,我們將這個位址所屬的pte算出來放到r0。
) w( f. L) C. p) q' sline 280~283,將要 map 的physical address的方式算出來放到r6。
4 e2 j* I( z# ?+ i5 t5 T+ G5 Tline 284,最後將結果存到r0所指到的pte。
$ U5 @. J& V/ c; a3 S% R+ N" L- c7 b" ?! S
以上三個動作,就是做好 ram 起頭的位址一開始map,還沒做完整塊map。由於我們目前的設定方式每塊是1MB,所以這邊是將RAM開始的1MB做map。' C. _* G; ~) x7 \; L0 H
5 F4 y. h: `+ W" C
line 327,返回,結束一開始的create page table的動作,我們可以看出其實並沒有做完整的初始化page table,所以之後應該會有其他page table的細節。
  1.     279         add     r0, r4, #PAGE_OFFSET >> 18
    * o  r" }' t7 f* @- ~
  2.     280         orr     r6, r7, #(PHYS_OFFSET & 0xff000000)
    1 J" Y& b3 i* w5 R) w
  3.     281         .if     (PHYS_OFFSET & 0x00f00000)2 h( r2 s8 J& _3 b. P4 W" a
  4.     282         orr     r6, r6, #(PHYS_OFFSET & 0x00f00000)
    # z/ D/ I+ h5 y8 R+ b+ |1 r
  5.     283         .endif
    % {* E4 o6 `4 g6 e( e: k
  6.     284         str     r6, [r0]- l; b/ ]' g* d& C& {. p% E- M
  7.     327         mov     pc, lr
複製代碼
附帶一提,我們這邊省略了一些用ifdef包起來的程式碼,像是一開始會印一些output message的程式碼(line286~326)。
14#
 樓主| 發表於 2008-10-22 20:37:08 | 只看該作者
自create page table返回後,我們偷偷看一下接下來的程式碼,+ k6 h8 F( Z# K  Y2 k1 S; C9 X
line 99, 將switch_data擺到r139 r  ^1 G) T+ s- e
line 101, 將enable_mmu擺到lr
6 r. V& V/ E& u$ s: Q9 bline 102, 將pc改跳到r10+PROCINFO_INITFUNC的地方去: i' z6 @$ r4 j: T0 f2 a

/ O* o- l6 p( ?# Q2 J其實這邊有點玄機,switch_data和enable_mmu都是function,結果位址都只是被存起來,並沒有直接跳過去執行。其實,雖然程式碼是順序switch_data->enable_mmu->proc init function,但其實執行的順序會是 procinfo 的init function-> enable_mmu -> switch_data 。至於,為什麼要這樣寫的原因?就不清楚了,也還沒仔細推敲過。
8 C/ e+ W5 Z: H
+ q$ t$ I2 @, g  X, V* Fswitch_data最後就會跳到大家都很熟悉的start_kernel(). 詳細要賣個關子,最近會忙一下,得要過一陣子才能貼文∼  
  1.      99         ldr     r13, __switch_data              @ address to jump to after
    ' F5 y' o4 ~. m1 _! C) g& O
  2.     100                                                 @ mmu has been enabled
    + T5 S  f1 x2 E6 a6 |
  3.     101         adr     lr, __enable_mmu                @ return (PIC) address
      Z2 ~  X, ^# @) L/ D
  4.     102         add     pc, r10, #PROCINFO_INITFUNC
複製代碼
15#
 樓主| 發表於 2009-7-4 01:09:36 | 只看該作者
老店重新開張~
9 |1 D9 m: E& U7 y$ U5 U4 F$ E, c& f" W4 r' k
花了一些時間把舊的貼文整理到一個blog
) S2 K& @1 ^5 `3 e* R有把一些敘述修改過
* l  t. F% O& R8 H希望會比較容易集中閱讀
. b, r! v- Y! l目前因為某些敘述不容易- T  A0 g! D4 k- T4 M0 r* Z
還是比較偏向筆記式而且用字不夠精確
; i/ p4 \3 W7 s( q希望之後能夠慢慢有系統地整理
  \$ }( J' V) X& e& f# s0 l* l大家有興趣的話
! [, V# h1 Z( @8 C- `可以來看看和討論
" k. l6 q$ I: [% [http://gogojesseco.blogspot.com/
  X1 Y$ c2 ^; |- f7 O9 Y+ n4 Q' o% P' q8 c  u7 y9 J$ ^
以後可能會採取  先在chip123貼新文章
  ]$ {5 L  s( F( y0 J慢慢整理到blog上的方式
/ ~# D- Q( N9 N: M% W( @因為chip123比較方便討論 =)
' l( s6 y$ {% m7 a, xblog編輯修改起來比較方便
& D( S* Z* E+ M) _' U. G/ m# `$ _閱讀也比較集中   大家可以在這邊看到討論
" u% W6 ?  m4 @然後在blog看到完整的文章 (類似BBS精華區的感覺)

評分

參與人數 1Chipcoin +5 +3 收起 理由
jacky002 + 5 + 3 感謝經驗分享!

查看全部評分

16#
 樓主| 發表於 2009-7-15 17:07:03 | 只看該作者
隔了很長一段時間沒update
5 ~& c1 }3 u  k" ]. e  x3 u之前程式碼走到 ./arch/arm/kernel/head.S 的 line 99 附近
  1.      99         ldr     r13, __switch_data              @ address to jump to after
    ) _$ z2 Z4 Z) e
  2.     100                                                 @ mmu has been enabled
    $ {5 Z* n; Z7 |5 ^# u) v# E
  3.     101         adr     lr, __enable_mmu                @ return (PIC) address5 M) V$ U' U3 ]0 H7 T6 @2 }9 K, o
  4.     102         add     pc, r10, #PROCINFO_INITFUNC
複製代碼
line 99, 將__switch_data放到r13。(留作之後用)8 E+ k+ K9 e4 o9 c) U+ J, e
line 101, 將__enable_mmu的addr放到lr。(留作之後用)
+ F1 h& c# B2 F2 c6 }0 _line 102, 將 r10+#PROCINFO_INITFUNC 放到pc,也就是jump過去的意思。r10是proc_info的位址。PROCINFO_INITFUNC則是用之前提過的技巧,指向定義在./arch/arm/mm/proc-xxx.S的資料結構,以arm926為例,最後會指到
  1. 463         b       __arm926_setup
複製代碼
所以程式碼就跳到了 __arm926_setup。
  1. 373         .type   __arm926_setup, #function$ o8 L  Y; v- w  N7 f
  2. 374 __arm926_setup:# V& d, F8 v% A1 w' f
  3. 375         mov     r0, #0% ~9 r7 z+ |0 \  s0 v5 p( {1 v' o
  4. 376         mcr     p15, 0, r0, c7, c7              @ invalidate I,D caches on v4
    & k! {9 ~/ V5 d. c/ d4 Z& m7 W9 K
  5. 377         mcr     p15, 0, r0, c7, c10, 4          @ drain write buffer on v42 O! R2 E9 W! F* H- I8 a/ E7 l
  6. 378 #ifdef CONFIG_MMU# [8 L% i. O6 s: l) z$ R
  7. 379         mcr     p15, 0, r0, c8, c7              @ invalidate I,D TLBs on v4
    - f. b( R- M, H) E. y' j& [
  8. 380 #endif; K7 S2 {# X+ `9 f/ @$ e; c
  9. 3 Z" f+ L; u: C0 r5 {1 p7 R& o7 ~
  10. 388         adr     r5, arm926_crval
    0 D/ A# Z9 _; U( F9 R
  11. 389         ldmia   r5, {r5, r6}0 Y- n" @0 x, E/ G' @
  12. 390         mrc     p15, 0, r0, c1, c0              @ get control register v4
    : J. I9 a1 r3 f7 |0 Z2 B
  13. 391         bic     r0, r0, r56 S) `* ?' z' s
  14. 392         orr     r0, r0, r6
    * m% A1 n! z) s' \

  15. 5 E$ q; u! u; n+ |; E# b7 r
  16. 396         mov     pc, lr$ b9 l2 o1 g1 {) o8 P
  17. 397         .size   __arm926_setup, . - __arm926_setup
複製代碼
這邊的程式碼就跟CPU有很大的相依性,3 Q# b( N- `+ r( Q  }; e
line 375~380, 主要就是invalidate CPU的I&D cache和清空write buffer。
% ]. e4 [- h4 |+ X2 W) q. P  kline 388~392, 把cp15的設定讀出來,並且將一些預設狀態做好運算放到r0。(預設值從arm926_crval這邊可以取得。)
! H; Z0 O! K+ s2 ]. E( F3 F# }line 396, 直接把pc跳到lr,因為我們之前已經將lr = enable_mmu,所以直接跳過去。
17#
 樓主| 發表於 2009-7-15 17:29:45 | 只看該作者
  1. 155 __enable_mmu:
    , c* s, w# _; G$ |. f: s6 {6 y1 C
  2. 170         mov     r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \! y* s! C: U: [
  3. 171                       domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \! P3 u9 V1 ^, u/ y
  4. 172                       domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
    ( s0 K* y; r# T3 g( ^2 D' c
  5. 173                       domain_val(DOMAIN_IO, DOMAIN_CLIENT))
    + `2 j& M; V. {( I8 \5 E
  6. 174         mcr     p15, 0, r5, c3, c0, 0           @ load domain access register
    . J: ?4 v- a( j; t7 _7 s( t1 [
  7. 175         mcr     p15, 0, r4, c2, c0, 0           @ load page table pointer
    & G% Q3 C3 K5 a6 K+ }
  8. 176         b       __turn_mmu_on
    1 v3 f& I5 L3 ^: r3 l) G
  9. 177 ENDPROC(__enable_mmu)
複製代碼
line 170~174,設置好domain access。(domain access可以先當成設置access的權限,或許以後可以寫詳細的文章說明)
3 `( _2 _5 ^+ i. n" H- \+ Jline 175~176,將create好的page table開頭丟給mmu。跳到turn_mmu_on
  1. 191 __turn_mmu_on:4 R0 o  @; J2 W! Z+ Z
  2. 192         mov     r0, r0
    , W$ a1 r+ L: T7 Y- {
  3. 193         mcr     p15, 0, r0, c1, c0, 0           @ write control reg
    3 Y2 R7 ]8 x, ~" W) a
  4. 194         mrc     p15, 0, r3, c0, c0, 0           @ read id reg/ S( V% Z3 z; G, L# o/ ?, W
  5. 195         mov     r3, r31 X) L  ]! g0 o9 E
  6. 196         mov     r3, r30 ]4 [: V3 M% y7 R
  7. 197         mov     pc, r13
    / i5 w% q8 ^& y5 S' O3 b6 ~
  8. 198 ENDPROC(__turn_mmu_on)
複製代碼
顧名思義就是把mmu打開,將我們準備好的r0設定交給mmu,並讀取id到r3,接著pc跳到r13,r13剛剛在head.S已經先擺好__switch_data。所以會跳到head-common.S。
  1. 18 __switch_data:
    7 L, [7 O4 j' U' P! T
  2. 19         .long   __mmap_switched! A' b" j; }* o+ E- A" q
  3. 20         .long   __data_loc                      @ r4
    / k4 [6 T" p, Y1 ^+ w$ [+ P) u6 e
  4. 21         .long   _data                           @ r50 m2 N, z% K  N- ^* r0 a$ m
  5. 22         .long   __bss_start                     @ r6+ v: b: W8 v+ Z  A3 z) v4 d
  6. 23         .long   _end                            @ r7
    0 W+ W  a( f5 I
  7. 24         .long   processor_id                    @ r4
    / W; B* }0 Z/ ?. q9 K. b% o! U
  8. 25         .long   __machine_arch_type             @ r58 x! B' z/ c2 c' q. E$ b1 F
  9. 26         .long   __atags_pointer                 @ r63 |3 ^% r) Y! m" _$ n  d: `
  10. 27         .long   cr_alignment                    @ r7
    + h! Y# v9 c  x: j
  11. 28         .long   init_thread_union + THREAD_START_SP @ sp5 J2 q3 ]5 n' o2 A" i+ T/ D4 s: N0 t
  12. 29- D- p% k; ~( l" W
複製代碼
switch_data的第一行就是 __mmap_switched,所以我們直接看line 39。
18#
 樓主| 發表於 2009-7-15 17:30:00 | 只看該作者
  1. 39 __mmap_switched:2 i5 g, e4 r! E* o$ {
  2. 40         adr     r3, __switch_data + 4
    " @9 |+ U6 N$ D: n( l+ K' t
  3. 41
    ) X0 n- u: z! K: N# j' Z) b3 s
  4. 42         ldmia   r3!, {r4, r5, r6, r7}
    # C; F1 U4 @) \6 g% V8 x
  5. 43         cmp     r4, r5                          @ Copy data segment if needed
    7 G* x7 [# ^, F' ^! h+ |5 ]
  6. 44 1:      cmpne   r5, r6
    , _, l2 H0 Y  E  _! Y
  7. 45         ldrne   fp, [r4], #4
    2 @0 S& F7 o4 Y5 K6 _7 K7 U
  8. 46         strne   fp, [r5], #47 s, k& s6 ]  Y
  9. 47         bne     1b* M3 j, ^( B3 u9 j1 U
  10. 48
    ! |+ g  m7 |* G' \
  11. 49         mov     fp, #0                          @ Clear BSS (and zero fp): y% v* E# C# o
  12. 50 1:      cmp     r6, r7; G: E0 g  O! b+ `9 D
  13. 51         strcc   fp, [r6],#40 }, C# U# Y# t! {: Q( `
  14. 52         bcc     1b
    6 h) s, y" u3 o; o5 D
  15. 53
    % y0 \- m( W1 G9 ]0 L
  16. 54         ldmia   r3, {r4, r5, r6, r7, sp}: m( l( W) \) X9 B+ z9 H" u0 h/ i
  17. 55         str     r9, [r4]                        @ Save processor ID- M% K2 S) N- k1 ]
  18. 56         str     r1, [r5]                        @ Save machine type: D% F, U, o& k5 f* M7 x
  19. 57         str     r2, [r6]                        @ Save atags pointer
    8 Z4 B6 F3 j4 ?( o0 ]6 ~7 d% [
  20. 58         bic     r4, r0, #CR_A                   @ Clear 'A' bit1 i6 U# p# i$ C8 k! L! T* d6 T
  21. 59         stmia   r7, {r0, r4}                    @ Save control register values( y- b1 X6 Z( u: [& h
  22. 60         b       start_kernel
    2 s- i0 l9 N# C
  23. 61 ENDPROC(__mmap_switched)
複製代碼
switch_data的第一行就是 __mmap_switched,所以我們直接看line 39。* o* z7 v/ m" _% ]# D- s
line 39,將__data_loc的addr放到r3
4 ?1 J3 @, C1 T! `/ S8 K! _2 eline 42,從r3的位址,連續讀取四筆資料到r4, r5, r6, r7
! }( W5 o! K: [* \, C1 tline 43~47,看看data segment是不是需要搬動。1 ?4 F8 o" L  v) x" \2 z
line 49~52, clear BSS。6 C, a2 E6 k7 ~0 g
, q& y: z! ]; `% x2 H# }+ y7 [
由於linux kernel在進入start_kernel前有一些前提必須要滿足:
2 N2 m* `* P* l" Fr0  = cp#15 control register
; Z4 p( y6 S$ Qr1  = machine ID: V9 a8 v# P! @- u4 G
r2  = atags pointer* V0 o) r: v& x3 P# X9 Y) X) u/ q& G$ G
r9  = processor ID
( c, t4 [! W. Z' T
5 x8 H8 T, q5 t6 g$ A所以line 54~59就是在做這些準備。
4 H8 P% c! J# g% R0 W$ q最後呢? 我們就跳到start_kernel了。(而且還是用b start_kernel,表示我們不會再回來了)' F" j# Y8 Z$ O4 Y1 V/ z, Z
* x$ r5 N$ ?' O7 a) H: B
看一下start_kernel()在./init/main.c,終於跳出architecture specific的目錄,表示
0 v# K! o$ S1 D我們真正的開始linux kernel的初始化。% Z! P. _4 d/ `/ W3 J
像是 shedule init, console init, memory init, irq init等等都在start_kernel裡頭。) E$ ]: c. u4 J( Y  V
到這邊之後,應該就可以深入linux kernel中,各項比較跟hardware不那麼相關的軟體部分。

評分

參與人數 1 +8 收起 理由
card_4_girt + 8 感謝經驗分享,希望你再接再厲!

查看全部評分

您需要登錄後才可以回帖 登錄 | 申請會員

本版積分規則

首頁|手機版|Chip123 科技應用創新平台 |新契機國際商機整合股份有限公司

GMT+8, 2024-5-17 06:55 AM , Processed in 0.149019 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表