Chip123 科技應用創新平台

 找回密碼
 申請會員

QQ登錄

只需一步,快速開始

Login

用FB帳號登入

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

trace linux kernel source - ARM - 03

[複製鏈接]
跳轉到指定樓層
1#
發表於 2008-10-7 14:00:18 | 顯示全部樓層 回帖獎勵 |倒序瀏覽 |閱讀模式
到目前為止,我們已經進展到kernel幫自己relocate完,並解將自己解壓縮到一個地方要準備開始執行,那疑問來了?到底是跳到哪裡去了,因為./compressed/head.S最後一行居然是$ I+ N# `" ]9 C7 V: d
『mov pc, r4』* ^8 Q% m8 K5 r
r4只代表了解壓縮完後kernel的位址,那究竟整包kernel編譯的時候,哪個function哪個東西被放在最前面咧?!- D( L* v6 d/ S+ ~
. z* h9 K6 @& m) p8 ?
所以我們又必須開始找於kernel link的時候是怎麼被安排的,有了前面的基礎,我們可以從Makefile知道程式碼如何被編譯。至於link上的細節,例如有那些section和section先後順序等等,可以從 lds 檔來規範。
/ k7 l5 S$ b) J# h+ y. u2 ?8 h8 c! i3 a  R6 D) R# o6 }6 {* J
有興趣的人可以看一下 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)
) o5 ?2 }* @  Y3 Q  C; v5 {我們可以發現第一個section是『.text.head』,裡頭的_stext從目前的位置開始放。
3 s# y# ?2 B2 l' i於是我們曉得只要找到屬於.text.head這個section,並且是_stext這個symbol的程式碼,就是解壓縮完後的第一行程式碼。
  1.      26     .text.head : {: P! K$ [, i) F" `& w. c% L
  2.      27         _stext = .;
    , L# n! Y$ n2 R% s6 j9 q# ?1 Y
  3.      28         _sinittext = .;- ]' D5 A+ K2 A- p$ _
  4.      29         *(.text.head)" ~/ @5 M4 s" x5 ^4 z; o
  5.      30     }
複製代碼
用指令搜尋一下,發現 ./arch/arm/kernel/head.S 裡頭有關鍵字(如下),結果我們從./arch/arm/boot/compressed/head.S跳到了./arch/arm/kernel/head.S
  1.      77     .section ".text.head", "ax", S" B+ r# J6 R7 S
  2.      78     .type   stext, %function; h4 p5 c5 X/ P3 M. ^& x! C
  3.      79 ENTRY(stext)
    ; N) O% A7 j% R$ F% Y* x  [, Y3 j
  4.      80     msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode  Y$ a% D5 b6 F( z
  5.      81                         @ and irqs disabled% e, D, b1 `" \
  6.      82     mrc p15, 0, r9, c0, c0      @ get processor id
    + c3 ^& X! V9 X- X# m! N  T
  7.      83     bl  __lookup_processor_type     @ r5=procinfo r9=cpuid8 P# `) w! {% T* u
  8.      84     movs    r10, r5             @ invalid processor (r5=0)?# R4 x* r" _' D% g- e/ R  J9 q
  9.      85     beq __error_p           @ yes, error 'p'
    ' W, L  @. R  O% B6 }' J3 L
  10.      86     bl  __lookup_machine_type       @ r5=machinfo0 a9 Z- e: u) l
  11.      87     movs    r8, r5              @ invalid machine (r5=0)?
    5 A$ t- v/ s, O4 t$ E7 \* w) v" x
  12.      88     beq __error_a           @ yes, error 'a'
    ; K3 W& l. x0 H% |" D
  13.      89     bl  __vet_atags) k5 ~% T( T# D1 }' a# k
  14.      90     bl  __create_page_tables
複製代碼
既然找到了檔案,我們又可以開始繼續。6 E1 B  k& U9 o
; {! n" \8 R2 J
看了一下,程式碼多了很多bl的跳躍動作,看來會在各個function跳來跳去。
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享分享 頂 踩 分享分享
2#
 樓主| 發表於 2008-10-9 15:32:16 | 顯示全部樓層
既然跳到真正的kernel開始跑,表示進入重頭戲,在進入kernel之前arm的平台有一些預設的狀況,也就是說arm kernel image會預設目前的cpu和系統的狀況是在某個狀態,這樣對一個剛要跑起來的OS比較決定目前要怎麼boot起來。9 F% K9 F: J+ z- w
# R" q" k. Z' S+ c" [+ V$ {! e
可以看一下comment,有清楚的描述。這也幫助我們了解為什麼decompresser的程式跑完之後,還要把cache關掉。
  1.      59 /*
    # k3 z. e0 X' C2 @3 A6 q
  2.      60  * Kernel startup entry point.
    + i& B5 _3 N2 o0 ~8 N7 ^/ ~
  3.      61  * ---------------------------
    # m1 @) P& m, D' Q( Q
  4.      62  *
    - b" n7 j1 [9 z0 f; F4 I5 h
  5.      63  * This is normally called from the decompressor code.  The requirements
    6 }2 P% M, R. Q  c
  6.      64  * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
    + [" R6 o$ Z& Y- O6 p0 n/ f6 V
  7.      65  * r1 = machine nr, r2 = atags pointer.
複製代碼
基於以上的假設,當program counter指到kernel的程式的時候,就會從line 80開始跑。
9 H. P0 P$ |* H4 S7 X4 F; wline 80, msr指令會把, operand的值搬到cpsr_c裡面。這是用來確保arm cpu目前是跑在svc mode, irq&fiq都disable。(設成0x1就會disable,definition在./include/asm-arm/ptrace.h)
( X& }- B. @) k9 yline 82, 讀取CPU ID到r9
" \, g) ?: b' vline 83, 跳到 __lookup_processor_type 執行(bl會記住返回位址)。
  1.      77     .section ".text.head", "ax"
    8 @( E: u9 g( D
  2.      78     .type   stext, %function/ c' y( I' z" V: B6 }
  3.      79 ENTRY(stext): L% t) s. a# n: \* G
  4.      80     msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
    - y- ]  d8 S, Q" C' `/ G) z0 y. e
  5.      81                         @ and irqs disabled! [& w! c! f3 P/ M# V2 b; ?
  6.      82     mrc p15, 0, r9, c0, c0      @ get processor id7 C& Z0 G/ K1 q' G
  7.      83     bl  __lookup_processor_type     @ r5=procinfo r9=cpuid
複製代碼
接著會跳到head-common.S這個檔,4 Y$ b- x# D8 y# K
line 158, (3f表示forware往前找叫做『3』label)將label 3的address放到r3。1 J7 \9 V% E$ p3 g
line 159, 將r3指到的位址的資料,依序放到r7, r6, r5.(ldmda的da是要每次都位址減一次)
  z3 w8 ?* A2 v) K, d% |0 sline l60, 161, 利用真實得到位址r3減去取得資料的位址得到一個offset值,這樣可計算出r5, r6真正應該要指到的地方。4 `3 K: C7 b& U* {# O  A8 p
line 163~169,這邊的程式應該不陌生,就是在各個CPU info裡面找尋對應的structure。找到的話就跳到line 171,返回head.S
! H1 g" x2 V2 S$ O" `4 R6 A3 dline 170, 找不到的話,r5的processor id就放0x0.表示unknown id。0 n! S/ B- d9 e  Y3 \
9 h' n' v+ s+ w  Z
__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
    2 ~6 w8 H$ L/ f7 X5 @
  2.     157 __lookup_processor_type:
    3 N! S, E: }1 B( T& N
  3.     158     adr r3, 3f$ |4 u6 w) v* i2 C7 J
  4.     159     ldmda   r3, {r5 - r7}
    0 s" t1 f8 A+ [  _" ~  a( X
  5.     160     sub r3, r3, r7          @ get offset between virt&phys
    6 h7 L8 T. n; k" [, q1 w
  6.     161     add r5, r5, r3          @ convert virt addresses to
    # E  z9 |: D6 e  K" k6 G
  7.     162     add r6, r6, r3          @ physical address space3 @8 x3 u: Y4 _, Y0 W
  8.     163 1:  ldmia   r5, {r3, r4}            @ value, mask
    / o4 d7 Q" t- P- x2 y3 q# D1 \
  9.     164     and r4, r4, r9          @ mask wanted bits
    # @9 s6 H. t+ h: H) m6 `
  10.     165     teq r3, r4, B# E4 ?+ L9 y& r7 [- q
  11.     166     beq 2f7 Z0 j! |) q$ u/ @5 H  a
  12.     167     add r5, r5, #PROC_INFO_SZ       @ sizeof(proc_info_list)
    1 L6 Z* Q: j. C. h* z
  13.     168     cmp r5, r6. j5 V  a6 L# p+ Q0 A. t7 l6 ?
  14.     169     blo 1b
      V9 B" b& e. ?) Q0 C: \+ c
  15.     170     mov r5, #0              @ unknown processor! R9 v1 D$ v$ F- l: B
  16.     171 2:  mov pc, lr+ e( C  B- j# \! V

  17. . O: L, L7 C6 l1 a. L
  18.     187     .long   __proc_info_begin
    6 w# I8 z' F/ j3 F5 V! u8 Q
  19.     188     .long   __proc_info_end7 \: o* k: l, S# B% Y1 P0 Q6 r% F
  20.     189 3:  .long   ./ D+ b/ ]: w1 {9 I( @: i
  21.     190     .long   __arch_info_begin, o; b* O' T4 Q; i) g
  22.     191     .long   __arch_info_end
複製代碼
跳了很多檔案,建議指令和object code如何link的概念要有,就會很清楚了。
3#
 樓主| 發表於 2008-10-13 17:20:35 | 顯示全部樓層
我們從 head-common.S返回之後,接著繼續看。  F0 k) [* B0 B4 e

6 s3 y) D* [/ Kline 84, movs的意思是說,做mov的動作,並且將指令的S bit設起來,最後的結果也會update CPSR。這個指令執行的過程當中,會根據你要搬動的值去把N and Z flag設好。這個是有助於下個指令做check的動作。
- r3 h5 g9 y: k5 A0 mline 85, 就是r5 = 0的話,就跳到__error_p去執行。( N( T; A9 M$ _& I
line 86, 跳到__lookup_machine_type。原理跟剛剛找proc的資料一樣。
  1.      83         bl      __lookup_processor_type         @ r5=procinfo r9=cpuid7 k. J( h/ s: b2 |: W; [0 _. x
  2.      84         movs    r10, r5                         @ invalid processor (r5=0)?1 C; V+ N0 b% k
  3.      85         beq     __error_p                       @ yes, error 'p'
    3 H. h& N$ x6 b+ u: _, a( G
  4.      86         bl      __lookup_machine_type           @ r5=machinfo
複製代碼
看得出來跟proc很像,有個兩個小地方是
2 Z. D& C% l  N" K, c  u
' p; `7 J0 \0 q, M1. line 207,用ldmia不是用ldmda。原因就在於存放arch 和 proc info 的位址剛好相反。一個在lable3的上面。一個在的下方。設計上應該是可以做修改的。
! K" P5 m& l, \4 z  g+ Q- f1 U  H$ G) U. l
2. arch定義的方式是透過macro,可以先看 ./include/asm-arm/mach/arch.h。裡頭有個 MACHINE_START ,這邊會設好macro,到時候直接使用就可以把arch的info宣告好。 例如 ./arch/arm/mach-omap1/board-generic.c
  1. /* macro */
    ) ~4 y; e6 I9 z' s3 d- n1 P
  2.      50 #define MACHINE_START(_type,_name)                      \; \: I$ x; M4 d* v# C
  3.      51 static const struct machine_desc __mach_desc_##_type    \
    ) m! T/ Y8 s, P* V7 E: W9 j8 ?
  4.      52  __used                                                 \
    4 N" L5 w' X  O% N
  5.      53  __attribute__((__section__(".arch.info.init"))) = {    \
    1 Y+ I* u; C4 ?7 u. N9 [
  6.      54         .nr             = MACH_TYPE_##_type,            \
    8 ~" k7 X9 N* d; e  S& k
  7.      55         .name           = _name,4 g; c, Z! v/ a
  8.      56
    5 d- z/ E# w$ f. `) P* y" b
  9.      57 #define MACHINE_END                             \
    + f! [) Z+ L0 q$ q1 e5 V$ }* @* _' ~
  10.      58 };2 U& j% R1 u( K
  11.      /* 用法 */$ j, `1 ]1 X8 p: f8 T
  12.      93 MACHINE_START(OMAP_GENERIC, "Generic OMAP1510/1610/1710")4 w( n) S+ n. u3 Q3 f" f0 f' ^9 U
  13.      94         /* Maintainer: Tony Lindgren <tony@atomide.com> */
    . v  _  Y  j$ J* ]$ W
  14.      95         .phys_io        = 0xfff00000,
    : r8 ]# w! @% U2 `
  15.      96         .io_pg_offst    = ((0xfef00000) >> 18) & 0xfffc,/ Z0 B5 T/ Z' p3 _; _' u. C: l" ]
  16.      97         .boot_params    = 0x10000100,5 f" {) J; I2 W' g2 [$ I5 P
  17.      98         .map_io         = omap_generic_map_io,) A1 B" i0 B; F% Q; X9 Q
  18.      99         .init_irq       = omap_generic_init_irq,
    / L8 U" P' B2 N3 U, F% x: i
  19.     100         .init_machine   = omap_generic_init,
    # X* p. D& W  F3 @) P
  20.     101         .timer          = &omap_timer,$ u2 {( b5 U8 c! \" r( H
  21.     102 MACHINE_END  j. m8 w/ a: o! B

  22. 1 h0 Q& E* Z2 J
  23.     /* func */2 W9 @0 f( I" C! i
  24.     204         .type   __lookup_machine_type, %function
    7 t+ |" L" q( X9 D
  25.     205 __lookup_machine_type:
    , {- ]* [. U/ u, [: E( ^, }
  26.     206         adr     r3, 3b
    6 t$ F4 ~" [$ C, c
  27.     207         ldmia   r3, {r4, r5, r6}0 P" B8 ~/ S! A4 M' L
  28.     208         sub     r3, r3, r4                      @ get offset between virt&phys: Q3 G1 r- d% @) l4 o8 r2 P, o
  29.     209         add     r5, r5, r3                      @ convert virt addresses to
    : ^, o7 Z! e1 p$ q
  30.     210         add     r6, r6, r3                      @ physical address space
    & o) R% S* U, l  W
  31.     211 1:      ldr     r3, [r5, #MACHINFO_TYPE]        @ get machine type
    , G+ E, S* ~+ o% T" a! `/ D
  32.     212         teq     r3, r1                          @ matches loader number?
    4 z+ d! N; q- ~" a1 L  N9 A
  33.     213         beq     2f                              @ found& q+ s1 t0 v% ~7 D, N
  34.     214         add     r5, r5, #SIZEOF_MACHINE_DESC    @ next machine_desc9 S$ q$ r6 y! Z4 M
  35.     215         cmp     r5, r6' ^& z5 R+ C3 |& }
  36.     216         blo     1b" Y2 c( |, B5 g9 s
  37.     217         mov     r5, #0                          @ unknown machine, U7 ]9 K+ V' m8 m0 O0 v( R  t
  38.     218 2:      mov     pc, lr
複製代碼
4#
 樓主| 發表於 2008-10-13 17:56:46 | 顯示全部樓層
接著我們又返回到head.S,5 r( }2 E2 G5 J) R% ?) H
line 87~88也是做check動作。
$ s) Z# A& L/ T! mline 89跳到vet_atags。在head-common.S
  1.      87         movs    r8, r5                          @ invalid machine (r5=0)?
    & n( w: Y& W5 s) R. U5 K
  2.      88         beq     __error_a                       @ yes, error 'a'9 _% C8 \) G' `/ e, L) a3 K
  3.      89         bl      __vet_atags9 u1 a- F! v- ^' _" ~# N6 m, m' L
  4.      90         bl      __create_page_tables
複製代碼
line 245, tst會去做and動作。並且update flags。這邊的用意是判斷位址是不是aligned。
* Z; w2 N. W: Zline 246, 沒有aligned跳到label 1,就返回了。
" M) }$ {9 u( d1 V" mline 248~250, 讀取atags所在的address裡頭的值到r5,看看是否不等於ATAG_CORE_SIZE,不等於的話也是返回。
6 x4 d, G% m; L" `( Eline 251~254, 判斷一下第一個達到的atag pointer是不是等於ATAG_CORE。如果正確的話,等一下要讀取atag的資料,才會正確。: m. f3 Q) v% z% l8 Z$ H& E
(atag是由bootloader帶給linux kernel的東西,用來告知booting所需要知道的參數。例如螢幕寬度,記憶體大小等等)
  1.      14 #define ATAG_CORE 0x544100015 Q1 r6 P5 Q2 [2 R( `( {7 j& j8 G* e
  2.      15 #define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
    4 ]% {- a8 }1 P: V: Y

  3. & h% M7 A5 l& `  f7 z
  4.     243         .type   __vet_atags, %function
    / M7 R* w+ |$ G# @4 w
  5.     244 __vet_atags:; a: D4 V- H/ ~6 g
  6.     245         tst     r2, #0x3                        @ aligned?
    4 \  h. t! R) f; M8 t
  7.     246         bne     1f0 o7 W3 r8 t2 Z' P  O8 W1 X
  8.     247
    . `8 K- C) `+ W+ h8 T
  9.     248         ldr     r5, [r2, #0]                    @ is first tag ATAG_CORE?
    - M7 i# `) [' ?7 P: S5 X: D
  10.     249         subs    r5, r5, #ATAG_CORE_SIZE
    ( @& E( K4 D2 ~" ^' F. p
  11.     250         bne     1f
    2 h( p8 T' s7 r; M6 f) |
  12.     251         ldr     r5, [r2, #4]
    ' P5 M9 v# Y9 D! D. V% H8 m2 r& B
  13.     252         ldr     r6, =ATAG_CORE
    1 Z, s9 g; R1 e; f5 H! }8 e
  14.     253         cmp     r5, r6, {0 y7 n3 N6 R" u, i
  15.     254         bne     1f8 {6 u( Z1 n: J% e  L
  16.     255/ a* v& V2 s4 ?. s4 U3 h
  17.     256         mov     pc, lr                          @ atag pointer is ok
    ! [$ }+ Z# ]" I
  18.     257
    5 ^& `+ [) m9 ], u% l/ R/ k
  19.     258 1:      mov     r2, #0! `# |1 s# q' q* f$ V0 ^4 T1 t
  20.     259         mov     pc, lr
複製代碼
接著我們又跳回去head.S。  
2 p& H! \6 y8 _# [line 90,又跳到 __create_page_tables。   (很累人....應該會死不少腦細胞)
) J% o0 o- E5 K. v0 d哇!page table?!!如雷貫耳的東西,不知道會怎麼做。剛剛偷看了一下,@@還蠻長了,先到這邊好了,下次在繼續寫。
5#
 樓主| 發表於 2008-10-14 12:13:51 | 顯示全部樓層
由於code看起來似乎越來越難解釋,會需要在檔案之間跳來跳去。建議一些基礎的東西可以再多複習幾次(其實是在說我自己 ),閱讀source code上收穫會比較多。例如:
3 ^5 }  c& a" y- H) o$ E. Q5 d* k% [% I" Z/ u6 T, D
1. arm instruction set - 這個最好每個指令的意思都大略看過一次,行有餘力多看幾個版本,armv4, v5 or v6。( w2 }$ D  D# J3 D+ C
2. compiler & assembler & linker - toolchain工具做的事情和功能,大致的流程和功能要有概念。
* \6 m4 u" q7 q$ m- X5 i3 o8 O1 f7 p3. Makefile & link script - 這兩個功能和撰寫的方式要有簡單的概念。+ E; d9 D5 L# i. Q/ i
5 E5 E. l% `5 h& X7 M
以上1是非常重要的重點,2&3只要有大致上的概念就可以,因為trace code的時候,有時需要跳到Makeflie&Link script去看最後object code編排的位址。' w! F# P6 X) [0 @

4 x4 y3 v, D# E! R# I! }; x由於我們trace到了page table這個關鍵字,在開始之前,稍微簡短的解釋,為了幫助了解,儘量用易懂的概念講,有些用詞可能會和一些真實狀況不同,但是懂了之後,應該會有能力分辨,至於正式而學術上解說,很多書上應該都有,或是google一下就很多啦∼
6#
 樓主| 發表於 2008-10-14 12:14:47 | 顯示全部樓層
page table本身是很抽象的東西,尤其是對所謂的 user-mode application programmer 來說,大部分的狀況它是不需要被考慮到的部份,但是他的產生卻對os和driver帶來了很多影響。我們從一個小小的疑問出發。; Y# m' M* f: }( d: ^4 D

2 Z$ o) X, m# _『產生page table到底是要給誰用的?』
: H7 G5 Y& W2 J* y. {" Y# @* a, R- {  c- a/ i& r% ?. c. ^
其實真正作用在它上面的H/W是MMU,一旦CPU啟用了MMU,當cpu嘗試去記憶體讀取一個operand的時候,cpu內部打出去的位址訊號都會先送到mmu,mmu會拿著這個位址去對照page table,看看這個位址是不是被轉換到另外一個位置(還會確認讀寫權力),最後才會到真正的位址去讀寫。5 D0 x8 B" |9 P$ O5 v5 p, ]

+ q2 S1 c) h# w8 L這樣來看CPU其實一開始打出去的位址訊號其實不是最後可以拿到資料的位址,我們稱為virtual address(va),mmu打出來的位址才能真正拿到資料,所以我們把mmu打出去的位址稱為physical address(pa)。那用來查詢這個位址對照關係的表格,就是page table。2 R: x/ s! T  N3 w7 P- \- t
0 ?' o- M  S: ~% J4 r( U; b2 W! f
到這邊我們有一個簡單的概念。來想像一個小問題,一個普通的周邊(例如你的顯示卡),因為他並不具有MMU功能,hw只看得懂pa,但是CPU卻是作用在va,假如我想去對hw的控制暫存器做讀寫,到底要用va還是pa?
7#
 樓主| 發表於 2008-10-14 12:15:51 | 顯示全部樓層
這時,寫driver的人就必須要小心,設定給硬體看的位址必須要先轉成pa(像是設定DMA),給CPU執行的程式碼要使用va,這對一開始嘗試寫driver但是又不瞭解va pa之間的差別的人,常常產生很多疑問。3 {/ o$ }, Z$ z. r% Z4 `6 ~' r4 t

& T' s" a; {& s2 V  R" `8 O" X" u現在我們回頭想想OS,既然我們跑到create page table,可以預期的是他想要將MMU打開,因此希望預先建立起一個page table,讓MMU知道目前os想規劃的位址對應和讀取權力是如何被安排的。所以os必須考慮到現在的系統究竟是長怎樣?應該要如何被安排?是不是有那些要被保護?好吧∼因為我們完全對os不了解,也不知道該安排什麼,只能祈禱在trace code完後,可以找到這些問題的答案,或者發現一些沒想到的問題。
* B4 G9 s4 V" O. ^' J: K9 A8 \% ^0 W+ I( k1 Q# b8 H0 w
知道了page table的大致上的功能,下篇就可以專心的研究這個table的長相,和它想規劃出的系統模樣。
0 V+ g# X- s, ~/ O9 a/ I) q! \. j6 Z
p.s. 字數限制好像變短了。   (看來很難寫)
8#
 樓主| 發表於 2008-10-14 13:56:17 | 顯示全部樓層
由於字數限制挑整,用詞儘量精簡,以便可以貼必要source。另外,以後寫到一段落,考慮收集成一篇完整的文章,這樣應就不會因為用回覆的方式,造成閱讀斷斷續續的問題。希望有人對文章呈現方式有想法的話,可以跟我講。
9#
 樓主| 發表於 2008-10-14 13:57:39 | 顯示全部樓層
現在,讓我們跳入create_page_tables吧∼9 u' t- _9 M3 l/ S
line 216,會跳到pgtbl的macro去執行,其實只是載入一個位址。
/ p) Q, n& N5 L! i: s3 K/ I
! c0 [) q) a" t* A+ i9 R- E2 R只是這個位址因為你硬體規劃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 */6 G  Q: S6 \. s4 R- \3 m% V
  2.      95 textofs-y       := 0x00008000( p1 Q: C( c- ]* W0 A* S
  3.     152 TEXT_OFFSET := $(textofs-y)
複製代碼
  1. /* include/asm-arm/arch-omap/memory.h */4 G7 j# L( u+ h7 h3 T9 w
  2.      40 #define PHYS_OFFSET             UL(0x10000000)' d1 Y: f& @% z7 F! }8 q- w
  3. " L; Y! P: N8 a; J
  4.      /* arch/arm/kernel/head.S */- b4 @3 S3 r9 N+ F4 y
  5.      29 #define KERNEL_RAM_VADDR        (PAGE_OFFSET + TEXT_OFFSET)
    - H" A' \/ B5 l* ^. C( k
  6.      30 #define KERNEL_RAM_PADDR        (PHYS_OFFSET + TEXT_OFFSET)/ u5 T- s0 P' A' H! H; U, F
  7. ' b% p* p3 O/ j, ^2 p5 Z
  8.      47         .macro  pgtbl, rd
    3 i. _( d! }% ^7 B" ^; s
  9.      48         ldr     \rd, =(KERNEL_RAM_PADDR - 0x4000)
    : T6 i9 e9 e' `2 [) g& N. R  v
  10.      49         .endm4 a9 U+ x0 |7 V
  11. 8 i. O9 H, p, i* }
  12.     216         pgtbl   r4                              @ page table address
複製代碼
10#
 樓主| 發表於 2008-10-14 14:16:53 | 顯示全部樓層
得到pg table的開始位置之後,當然就是初始化囉。
1 a: F& r5 s% s6 V5 }* Y4 mline 221, 將pg table的base addr放到r0.1 {9 w3 R8 L4 q, I
line 223, 將pg table的end addr放到r6.
0 v" L( X/ @0 r0 R5 Q; t* Cline 224~228, 反覆地將0x0寫到pg table的區段裡頭,每個loop寫16bytes. (4x4),直到碰到end,結果就是把它全部clear成0x0.
  1.     221         mov     r0, r4
    ! h8 w- q3 `/ X6 s# t4 l" l
  2.     222         mov     r3, #0  t& `  r# ]2 N1 C% G* F* m
  3.     223         add     r6, r0, #0x4000
    4 H/ [9 @/ v# M; L
  4.     224 1:      str     r3, [r0], #4( {" ^0 ?6 P( N. K2 z. V9 {
  5.     225         str     r3, [r0], #4
    0 o+ j/ n9 F2 t1 U* P6 Q0 T9 P( R
  6.     226         str     r3, [r0], #42 q- h5 P0 d$ L9 @0 L+ G, I
  7.     227         str     r3, [r0], #4
    % }& x' A1 z# Z4 A1 \1 n) b
  8.     228         teq     r0, r61 B6 N% `3 `% D
  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 | 顯示全部樓層
問題怎麼填值??, @) `3 `2 U( O! N3 b5 b
拿出ARM的手冊,翻到MMU章節。一看發現page有很多種,還得分first level和second level。 1 X* @+ P# d6 F

# e. A0 D9 K8 c" W# j3 p* r; G念書時的印象,是從1st level在去查2nd level,先看怎麼設定1st level (不知以前老師有沒有亂教)
) g1 Y9 Q7 h! Q! H* A4 F1. [31:20]存著section base addr5 r# [8 @3 z3 w* b3 i  ~4 _9 H
2. [19:2]存著mmu flags5 o5 c/ c8 a+ j/ L9 u. n  x5 O
3. [1:0]用來辨別這是存放哪種page, 有四種:8 |- y" ^0 T  b* W
   a. fault (00) b. coarse page (01) c. section (1st level) (10) d. fine page (11)
9 D& i7 M2 ^- z- P0 o. M9 b4. pg tabel資料要存放到 [31:14] = translation base, [13:2] = table index , [1:0] = 0b00 的位址9 z) W; _& [, Q/ l4 L! Y3 G' `' s4 c
, o* B' L3 M" m' l$ `- i$ K1 Y
來看code是怎麼設定。
  M/ ^$ Q7 B+ e% D. o0 l
8 v* i5 ~- x, W$ dline 239, 將pc的值往右shift 20次放到r6.這是取得前20高位元的資料,有點像是 1.。5 l# [! s. a2 _7 b+ Y$ G2 G
line 240, 將r6往左shift 20次之後,和r7做or的動作,剛好也是 2.提到的mmu flags和1.處理好的資料做整理。1 |( B" B- o, |
所以前面兩個做完,就完成了bit[31:2]。
" U7 {& x0 G" Tline 241, 將r3的資料寫到r4+(r6<<0x2)的地方去,剛好是4.提到的位址。(lucky)
  1.     239         mov     r6, pc, lsr #20
    + F' A% [& X$ h% N& q
  2.     240         orr     r3, r7, r6, lsl #20
    ) k1 F. m" \+ S4 v
  3.     241         str     r3, [r4, r6, lsl #2]
複製代碼
p.s. 用pc值剛好可以算出當前的page。
12#
 樓主| 發表於 2008-10-22 19:47:03 | 顯示全部樓層
最近又被釘上了,開始忙碌,大概沒太多時間貼文∼* J1 h7 Q5 o: H( r5 c
, _. S/ s$ G. c
上篇已經將pc所屬的page table entry(pte)設定好,接著繼續看* h+ {, g" [* r6 U- D+ x
line 247, 248, 將KERNEL_START的位址往右shift 18算出pte的offset,(等於line239&241,239&241是先shift right 20然後shift left 2),並將剛剛r3的值設給pte。『!』的意思是會把address存到r0。
" e3 K5 f4 W: _. T
# i; c7 X5 ]  u9 O( Cline 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) >> 18: y. T0 ]8 w& G1 `3 X( V
  2.     248         str     r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]! 0 j/ V" \, N9 R( F2 }5 t4 r
  3.     249         ldr     r6, =(KERNEL_END - 1)( x) G0 n1 n2 M  Q& h6 `
  4.     250         add     r0, r0, #40 s+ Z7 M1 h( v
  5.     251         add     r6, r4, r6, lsr #18
    ! ^) H: U/ ]; s7 u. l: q  F# r
  6.     252 1:      cmp     r0, r60 ~2 b+ h. Q* F; f
  7.     253         add     r3, r3, #1 << 20/ ~7 y; {4 o; Z; I
  8.     254         strls   r3, [r0], #4
    . c5 g! ]8 `( v8 {1 g6 w4 N
  9.     255         bls     1b
複製代碼
13#
 樓主| 發表於 2008-10-22 20:24:58 | 顯示全部樓層
line279,PAGE_OFFSET是規範RAM mapping完後的virtual address,我們將這個位址所屬的pte算出來放到r0。
7 F2 D  d4 {$ R4 Q7 qline 280~283,將要 map 的physical address的方式算出來放到r6。6 e  C3 E4 v; G  @/ x4 E, s- D) e& j4 A
line 284,最後將結果存到r0所指到的pte。+ c* G' P2 t( W& R, y
5 _: q# R0 y, t8 H+ _# [
以上三個動作,就是做好 ram 起頭的位址一開始map,還沒做完整塊map。由於我們目前的設定方式每塊是1MB,所以這邊是將RAM開始的1MB做map。# S3 i* W! B( x, J$ w

8 ~# U1 W6 d5 W0 }/ ?( u# Aline 327,返回,結束一開始的create page table的動作,我們可以看出其實並沒有做完整的初始化page table,所以之後應該會有其他page table的細節。
  1.     279         add     r0, r4, #PAGE_OFFSET >> 18
    4 W; b9 P4 b( G: q& u! r% G; d
  2.     280         orr     r6, r7, #(PHYS_OFFSET & 0xff000000)8 t8 I+ d# I  S" N* K
  3.     281         .if     (PHYS_OFFSET & 0x00f00000)
    - k0 a3 u: A; ]1 L( e# R, D
  4.     282         orr     r6, r6, #(PHYS_OFFSET & 0x00f00000)) j  M9 A* F1 q! |; Q) O
  5.     283         .endif
    2 o4 w- r: [$ Y  K& H+ J
  6.     284         str     r6, [r0]" @0 L, Z- p$ u
  7.     327         mov     pc, lr
複製代碼
附帶一提,我們這邊省略了一些用ifdef包起來的程式碼,像是一開始會印一些output message的程式碼(line286~326)。
14#
 樓主| 發表於 2008-10-22 20:37:08 | 顯示全部樓層
自create page table返回後,我們偷偷看一下接下來的程式碼,( W3 n4 w3 _4 K1 J% \; ~7 x' t
line 99, 將switch_data擺到r13: [9 w# G. h. ^8 F. A
line 101, 將enable_mmu擺到lr. |( g; w5 w! R0 N! Y1 r
line 102, 將pc改跳到r10+PROCINFO_INITFUNC的地方去
1 M) G0 }" i" E1 ?4 k0 f) p$ e9 s" h
7 }6 x0 C; l; G1 `# P3 ?% ?9 s8 o其實這邊有點玄機,switch_data和enable_mmu都是function,結果位址都只是被存起來,並沒有直接跳過去執行。其實,雖然程式碼是順序switch_data->enable_mmu->proc init function,但其實執行的順序會是 procinfo 的init function-> enable_mmu -> switch_data 。至於,為什麼要這樣寫的原因?就不清楚了,也還沒仔細推敲過。
! E/ p6 [+ L3 z3 a, M3 _) V6 d5 @+ J5 F$ o. o2 i
switch_data最後就會跳到大家都很熟悉的start_kernel(). 詳細要賣個關子,最近會忙一下,得要過一陣子才能貼文∼  
  1.      99         ldr     r13, __switch_data              @ address to jump to after
    % F2 \' B% \4 U
  2.     100                                                 @ mmu has been enabled
    " m; x" k0 i9 _# b, z# H' J
  3.     101         adr     lr, __enable_mmu                @ return (PIC) address
    / ?4 X5 U7 n* z( }7 f
  4.     102         add     pc, r10, #PROCINFO_INITFUNC
複製代碼
15#
 樓主| 發表於 2009-7-4 01:09:36 | 顯示全部樓層
老店重新開張~! ^/ ~# Z* A8 S4 k# O& k

  B3 F! ~# i5 d5 O3 Z8 o9 y% ]9 \花了一些時間把舊的貼文整理到一個blog
8 k7 q  ^5 O5 F  L9 x有把一些敘述修改過- H4 |% K, A6 ^  B0 o  H5 B8 m7 \
希望會比較容易集中閱讀
* j$ z  A; }. m) q3 m8 `- K  j+ ?4 a目前因為某些敘述不容易
7 I: S+ R6 j0 [+ ^還是比較偏向筆記式而且用字不夠精確
3 R8 E+ `) @6 a8 m5 J# A, [希望之後能夠慢慢有系統地整理
( m5 t4 P  e! J1 f( ~3 M大家有興趣的話+ l! p/ I* ~% V/ Y3 y5 P
可以來看看和討論 ' G4 |& Y: G- g! G) P  x
http://gogojesseco.blogspot.com/
4 F/ |8 r$ ~* ~! B2 t4 T
; T! V& m; R% `7 r以後可能會採取  先在chip123貼新文章8 Z; t: K1 R" S, Q$ j3 q
慢慢整理到blog上的方式0 `: W; e- m0 d8 D8 Q* z1 }
因為chip123比較方便討論 =)
4 ?% T. l& |8 S6 r2 Cblog編輯修改起來比較方便) R  {# z- T8 ?4 e+ i1 s+ s
閱讀也比較集中   大家可以在這邊看到討論
3 i9 S" w& o4 V然後在blog看到完整的文章 (類似BBS精華區的感覺)

評分

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

查看全部評分

16#
 樓主| 發表於 2009-7-15 17:07:03 | 顯示全部樓層
隔了很長一段時間沒update
) B  u# ?/ |" a$ u之前程式碼走到 ./arch/arm/kernel/head.S 的 line 99 附近
  1.      99         ldr     r13, __switch_data              @ address to jump to after
    . A, r$ L% q8 b( s" o! I
  2.     100                                                 @ mmu has been enabled
    0 K* K! D- _$ J
  3.     101         adr     lr, __enable_mmu                @ return (PIC) address8 m! i' I0 r( z
  4.     102         add     pc, r10, #PROCINFO_INITFUNC
複製代碼
line 99, 將__switch_data放到r13。(留作之後用)
; c( r3 Q5 o6 x% a) {* a, R2 [8 l$ Kline 101, 將__enable_mmu的addr放到lr。(留作之後用)
( ]+ M7 l7 Y; a0 l5 {; p4 j: bline 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, #function1 |: @' h* c) }
  2. 374 __arm926_setup:. X: p/ i9 o" j5 e" B& e
  3. 375         mov     r0, #0
    ! A9 p% @1 J; n& K, Y
  4. 376         mcr     p15, 0, r0, c7, c7              @ invalidate I,D caches on v4. ^3 k0 Z, Z6 ?% [: `! B4 V- o! |
  5. 377         mcr     p15, 0, r0, c7, c10, 4          @ drain write buffer on v4
      k5 @( H# I9 G# ]! X: S
  6. 378 #ifdef CONFIG_MMU
    + G' c, r+ p; h; u: _& K4 Z
  7. 379         mcr     p15, 0, r0, c8, c7              @ invalidate I,D TLBs on v46 d/ E6 `: [# r; H, b
  8. 380 #endif
    + l( H3 ?. W6 B& z# o
  9. & m, N# R7 A  A; ^/ W- V
  10. 388         adr     r5, arm926_crval$ S/ p/ e0 [  k3 K- O1 I3 B, O
  11. 389         ldmia   r5, {r5, r6}
    ' z* t0 C* C! N( G6 R7 `
  12. 390         mrc     p15, 0, r0, c1, c0              @ get control register v47 i( R5 `% n+ T" K1 e; r( `! u
  13. 391         bic     r0, r0, r5
    9 E6 N7 F: I  |; ?0 R
  14. 392         orr     r0, r0, r6
    3 o' n9 O& @! \; a: y4 [2 h
  15. 3 X% l6 w& o: W' @9 C* ~% _. [
  16. 396         mov     pc, lr) t4 a" D: X- r3 R* R# X! Z
  17. 397         .size   __arm926_setup, . - __arm926_setup
複製代碼
這邊的程式碼就跟CPU有很大的相依性,$ n$ m* W6 C# t6 \
line 375~380, 主要就是invalidate CPU的I&D cache和清空write buffer。
2 \0 s0 S. E  t8 \: y6 t- {# W) n# Eline 388~392, 把cp15的設定讀出來,並且將一些預設狀態做好運算放到r0。(預設值從arm926_crval這邊可以取得。)
5 C3 ]! e6 \- r. z& }0 `line 396, 直接把pc跳到lr,因為我們之前已經將lr = enable_mmu,所以直接跳過去。
17#
 樓主| 發表於 2009-7-15 17:29:45 | 顯示全部樓層
  1. 155 __enable_mmu:; c, I- l! b; v' b
  2. 170         mov     r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \7 ]$ y1 n4 S. s& |2 R0 q5 O
  3. 171                       domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
    - r3 i$ S- }* |! s2 X2 u! i/ o
  4. 172                       domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \) B% L( `0 o4 p2 O7 U
  5. 173                       domain_val(DOMAIN_IO, DOMAIN_CLIENT))
    # j% K/ s0 Z! W& Y1 r0 n
  6. 174         mcr     p15, 0, r5, c3, c0, 0           @ load domain access register
    : B3 k" j2 I1 E! d7 {1 Y8 Y
  7. 175         mcr     p15, 0, r4, c2, c0, 0           @ load page table pointer, `, o4 T- O9 B" u% k' S' O" c" x; k
  8. 176         b       __turn_mmu_on6 h0 J' X# f1 B6 P1 n9 x5 E* {
  9. 177 ENDPROC(__enable_mmu)
複製代碼
line 170~174,設置好domain access。(domain access可以先當成設置access的權限,或許以後可以寫詳細的文章說明)
4 \! t4 W* ^) G& Eline 175~176,將create好的page table開頭丟給mmu。跳到turn_mmu_on
  1. 191 __turn_mmu_on:
    # @& L, W9 `3 L" ?! Q
  2. 192         mov     r0, r0
      K, s* m, k& O/ _9 Z% P7 e
  3. 193         mcr     p15, 0, r0, c1, c0, 0           @ write control reg* S5 E$ n# t1 O" i1 C9 y/ S- T
  4. 194         mrc     p15, 0, r3, c0, c0, 0           @ read id reg# H  o4 l$ ~/ r' A. V! x0 @. J
  5. 195         mov     r3, r32 l! M5 M7 t9 R1 H; j& Y5 z
  6. 196         mov     r3, r3( \# C: H' [& i- B
  7. 197         mov     pc, r135 K! z' l7 i/ L
  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:4 `% ~7 T0 k: _' }6 x
  2. 19         .long   __mmap_switched) G, @$ ?$ |& r& V$ G
  3. 20         .long   __data_loc                      @ r4
    7 V2 ~3 F) |2 E
  4. 21         .long   _data                           @ r5
    # y" d4 n' C: T' B' \) h# Q
  5. 22         .long   __bss_start                     @ r6
    7 Z1 o9 @# B! m0 k* e
  6. 23         .long   _end                            @ r77 ~' D& z" ~( x! R8 w/ c! C
  7. 24         .long   processor_id                    @ r45 J- l+ A1 t9 m8 n  p  Q: P. c
  8. 25         .long   __machine_arch_type             @ r5
    7 \, g, r/ Y. z/ M* W
  9. 26         .long   __atags_pointer                 @ r6
    ; h* ^- o7 C2 Q
  10. 27         .long   cr_alignment                    @ r7' q* a9 Y7 J! ^: E& L  F
  11. 28         .long   init_thread_union + THREAD_START_SP @ sp
    7 F" d% v! x" r1 E- a
  12. 29
    # N  b1 H8 S( j$ p  J1 I6 x
複製代碼
switch_data的第一行就是 __mmap_switched,所以我們直接看line 39。
18#
 樓主| 發表於 2009-7-15 17:30:00 | 顯示全部樓層
  1. 39 __mmap_switched:
    , Z+ l' j: s6 i: p
  2. 40         adr     r3, __switch_data + 4! ~' `# N7 S2 X: ~5 A! K5 h
  3. 41
    1 B0 Y8 `$ ?  A; q. \4 U
  4. 42         ldmia   r3!, {r4, r5, r6, r7}4 r4 F/ p: I9 S
  5. 43         cmp     r4, r5                          @ Copy data segment if needed; ]! t# f1 [0 h1 S" l
  6. 44 1:      cmpne   r5, r6
    * d- c6 V% g1 v7 y' U
  7. 45         ldrne   fp, [r4], #4  `5 A3 s8 V) r/ c' r
  8. 46         strne   fp, [r5], #4
    ) I# V$ P, S3 f5 T4 f
  9. 47         bne     1b2 C1 w" Y) t& _% W1 L7 L- |- ?
  10. 485 ^, F2 x' \% M# z1 S; c
  11. 49         mov     fp, #0                          @ Clear BSS (and zero fp)
    # j6 W* f6 u' F: U9 ]; W$ Z
  12. 50 1:      cmp     r6, r7
    2 G$ X9 h5 K% {) |$ U* i
  13. 51         strcc   fp, [r6],#4
    5 L8 ]0 v/ G  _$ a1 u; O  I9 q0 ?
  14. 52         bcc     1b4 N; L" _+ P! h6 g0 Y( s8 ]/ b
  15. 53# C. ?3 Y" I; g  h6 q
  16. 54         ldmia   r3, {r4, r5, r6, r7, sp}, W1 d7 M7 R9 Q
  17. 55         str     r9, [r4]                        @ Save processor ID
    - d, u% D) M8 x) d% i' }
  18. 56         str     r1, [r5]                        @ Save machine type; l2 d' @: Y2 E- _: j" J% g
  19. 57         str     r2, [r6]                        @ Save atags pointer
    1 w0 R4 u1 i1 I  [% v* s
  20. 58         bic     r4, r0, #CR_A                   @ Clear 'A' bit
    0 S) t& }- {' h$ \5 {  z" I
  21. 59         stmia   r7, {r0, r4}                    @ Save control register values. O3 R, Q% x# k1 m* v- \  m0 ?
  22. 60         b       start_kernel
    3 `8 ^' r. j; F! J! @
  23. 61 ENDPROC(__mmap_switched)
複製代碼
switch_data的第一行就是 __mmap_switched,所以我們直接看line 39。  S0 y5 {! }6 F6 C2 R' n4 C3 Q
line 39,將__data_loc的addr放到r3  r* P1 M: G; W  Y1 e% c' X; D
line 42,從r3的位址,連續讀取四筆資料到r4, r5, r6, r7
( q6 |* m$ y! I- t: H+ m! Xline 43~47,看看data segment是不是需要搬動。6 }7 e2 p. ]" J/ A- s
line 49~52, clear BSS。
! y: K1 s# Z! m# |5 b  n. @7 y% q: u( A
由於linux kernel在進入start_kernel前有一些前提必須要滿足:
0 {8 E0 d4 f" B1 ir0  = cp#15 control register* o% t1 m. ]/ M2 F3 b$ b
r1  = machine ID9 C! Q! u- e8 L) [! C; s: o4 c
r2  = atags pointer
8 `- H  f# l$ H3 vr9  = processor ID8 W' U# H% @- w- ^- }& r
" O3 n7 l- C4 B  `. k7 B& {& y" m* d
所以line 54~59就是在做這些準備。" ~' D: O* ~9 q( g2 q; w& `# t
最後呢? 我們就跳到start_kernel了。(而且還是用b start_kernel,表示我們不會再回來了)+ k6 S! d2 P' i0 ?

7 f! [8 A2 H# k, b7 Y7 h$ n看一下start_kernel()在./init/main.c,終於跳出architecture specific的目錄,表示1 b( n& n9 F2 h( I" ?" ^
我們真正的開始linux kernel的初始化。, g0 o6 g2 {0 s+ ]! k- d- s
像是 shedule init, console init, memory init, irq init等等都在start_kernel裡頭。
0 m2 V2 A, C9 |" N8 z2 k0 g$ l/ K到這邊之後,應該就可以深入linux kernel中,各項比較跟hardware不那麼相關的軟體部分。

評分

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

查看全部評分

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

本版積分規則

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

GMT+8, 2024-5-18 04:02 PM , Processed in 0.139518 second(s), 19 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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