Chip123 科技應用創新平台

 找回密碼
 申請會員

QQ登錄

只需一步,快速開始

Login

用FB帳號登入

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

trace linux kernel source - ARM - 03

[複製鏈接]
跳轉到指定樓層
1#
發表於 2008-10-7 14:00:18 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
到目前為止,我們已經進展到kernel幫自己relocate完,並解將自己解壓縮到一個地方要準備開始執行,那疑問來了?到底是跳到哪裡去了,因為./compressed/head.S最後一行居然是9 y. g* f1 w# D4 @5 p' V9 X
『mov pc, r4』, i, k6 Z$ F  O8 ^8 X% v. y/ n
r4只代表了解壓縮完後kernel的位址,那究竟整包kernel編譯的時候,哪個function哪個東西被放在最前面咧?!" l' t' c7 e1 g+ M0 \/ Q: j

2 _, m% K# H7 H3 F# v( ?% c所以我們又必須開始找於kernel link的時候是怎麼被安排的,有了前面的基礎,我們可以從Makefile知道程式碼如何被編譯。至於link上的細節,例如有那些section和section先後順序等等,可以從 lds 檔來規範。* u; ~# i" ^4 T
* ^6 A& X, z2 }0 X  U9 u# A) F( \
有興趣的人可以看一下 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)
: \4 S8 ^- ^+ I) ^; f我們可以發現第一個section是『.text.head』,裡頭的_stext從目前的位置開始放。
" d: E0 \( N: g1 w1 y於是我們曉得只要找到屬於.text.head這個section,並且是_stext這個symbol的程式碼,就是解壓縮完後的第一行程式碼。
  1.      26     .text.head : {
    : |# \2 J: A' |" G9 c
  2.      27         _stext = .;( }& }% W) `7 G: l
  3.      28         _sinittext = .;
      A4 t6 J# P+ ?' S
  4.      29         *(.text.head)
    ' k- \9 R* I+ w* A2 Z; {- Z$ @
  5.      30     }
複製代碼
用指令搜尋一下,發現 ./arch/arm/kernel/head.S 裡頭有關鍵字(如下),結果我們從./arch/arm/boot/compressed/head.S跳到了./arch/arm/kernel/head.S
  1.      77     .section ".text.head", "ax"& \( e* m) \: ]9 o' D
  2.      78     .type   stext, %function
    7 G! P" Z7 w5 I* A
  3.      79 ENTRY(stext)
    0 A, }. K1 I5 O7 D; a
  4.      80     msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
    : f, ]& j% I5 o6 O2 M4 [3 |
  5.      81                         @ and irqs disabled' q" _' i, n+ t2 M% _% k/ p
  6.      82     mrc p15, 0, r9, c0, c0      @ get processor id. P9 O0 r( }% @9 o# {7 y7 i+ |; H$ s
  7.      83     bl  __lookup_processor_type     @ r5=procinfo r9=cpuid$ j8 @+ W& E& y) z" i; z, L
  8.      84     movs    r10, r5             @ invalid processor (r5=0)?) e' U8 z$ s# ~" Y/ {
  9.      85     beq __error_p           @ yes, error 'p'
    ( v( t. E6 ~/ G. v" i8 e
  10.      86     bl  __lookup_machine_type       @ r5=machinfo8 _2 m# i0 i& _1 B: N
  11.      87     movs    r8, r5              @ invalid machine (r5=0)?
    # u, H# W' n4 {- T4 S3 @& A- G
  12.      88     beq __error_a           @ yes, error 'a'
    4 Y0 h0 W, v: K' A4 N1 q2 t
  13.      89     bl  __vet_atags
    % s% [( H. Y0 G$ s3 b+ h
  14.      90     bl  __create_page_tables
複製代碼
既然找到了檔案,我們又可以開始繼續。9 E# r! a6 q3 r) k0 E2 V6 ?

* b4 l; p; R0 v9 @看了一下,程式碼多了很多bl的跳躍動作,看來會在各個function跳來跳去。
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享分享 頂 踩 分享分享
2#
 樓主| 發表於 2008-10-9 15:32:16 | 只看該作者
既然跳到真正的kernel開始跑,表示進入重頭戲,在進入kernel之前arm的平台有一些預設的狀況,也就是說arm kernel image會預設目前的cpu和系統的狀況是在某個狀態,這樣對一個剛要跑起來的OS比較決定目前要怎麼boot起來。: W2 n/ X0 v+ F+ Y4 z
/ g5 A( y/ z3 w+ j& I
可以看一下comment,有清楚的描述。這也幫助我們了解為什麼decompresser的程式跑完之後,還要把cache關掉。
  1.      59 /*
    , c) G9 M0 d8 x% A- v
  2.      60  * Kernel startup entry point.: p; \* y  t6 c# J
  3.      61  * ---------------------------1 C( D0 J6 I: {* z' E5 G
  4.      62  *
    , C, V+ @( p9 W5 X& {0 j2 F( p
  5.      63  * This is normally called from the decompressor code.  The requirements
    , `2 c+ s2 ~  L9 {& I' W3 Y0 m/ i2 Q
  6.      64  * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,: e& y" ~6 l$ c6 o+ u2 ~3 |
  7.      65  * r1 = machine nr, r2 = atags pointer.
複製代碼
基於以上的假設,當program counter指到kernel的程式的時候,就會從line 80開始跑。
7 z. y. A0 \* i1 C1 n  U/ |line 80, msr指令會把, operand的值搬到cpsr_c裡面。這是用來確保arm cpu目前是跑在svc mode, irq&fiq都disable。(設成0x1就會disable,definition在./include/asm-arm/ptrace.h)( G" K4 f( ~1 \: b$ [/ P
line 82, 讀取CPU ID到r9& h4 Q4 m" n8 d" o. l# X
line 83, 跳到 __lookup_processor_type 執行(bl會記住返回位址)。
  1.      77     .section ".text.head", "ax"
    ; g- {, S% ?: B% w) p6 I3 ]
  2.      78     .type   stext, %function4 I0 {; f( Z" h; u4 J" f. H1 B
  3.      79 ENTRY(stext)
    " [& H" k3 D4 o3 f. D( }) \
  4.      80     msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
    . y: k3 d8 a1 i, ]; e% r- `
  5.      81                         @ and irqs disabled% Q9 o8 t, j# F6 }
  6.      82     mrc p15, 0, r9, c0, c0      @ get processor id
    ' C, f7 _, L  ]' K* J7 p
  7.      83     bl  __lookup_processor_type     @ r5=procinfo r9=cpuid
複製代碼
接著會跳到head-common.S這個檔,
) {6 f3 l7 i7 P4 ^line 158, (3f表示forware往前找叫做『3』label)將label 3的address放到r3。
& z+ F; g0 m$ V- Nline 159, 將r3指到的位址的資料,依序放到r7, r6, r5.(ldmda的da是要每次都位址減一次)
, y" c9 ?6 x( _2 j: {0 fline l60, 161, 利用真實得到位址r3減去取得資料的位址得到一個offset值,這樣可計算出r5, r6真正應該要指到的地方。9 c- J, \7 I( S
line 163~169,這邊的程式應該不陌生,就是在各個CPU info裡面找尋對應的structure。找到的話就跳到line 171,返回head.S
$ {- J/ B( t4 ^' C, B5 \7 wline 170, 找不到的話,r5的processor id就放0x0.表示unknown id。# I/ B- ], D+ P4 `7 X: j
/ S, g" ~" G/ I2 W5 t
__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) p: U0 U7 L  E
  2.     157 __lookup_processor_type:, i7 C' A  R- y4 r: ?% q
  3.     158     adr r3, 3f
    " `( E8 X7 f# k- X6 M& a; b
  4.     159     ldmda   r3, {r5 - r7}
    & x8 C  x; S) ?2 m! c5 }8 f  m
  5.     160     sub r3, r3, r7          @ get offset between virt&phys
    . L7 i# T4 t7 l7 S. z
  6.     161     add r5, r5, r3          @ convert virt addresses to+ |9 Y; s6 ]; i2 p
  7.     162     add r6, r6, r3          @ physical address space  l0 g2 I: ?2 V: N3 B! J
  8.     163 1:  ldmia   r5, {r3, r4}            @ value, mask
    7 \, `5 m; ^  D& ?$ d) v5 e) k6 ^
  9.     164     and r4, r4, r9          @ mask wanted bits/ K! p6 z- h. X" z: O/ n
  10.     165     teq r3, r4% F0 p  E5 G( Y+ ]( y
  11.     166     beq 2f% L. j  {# j  f
  12.     167     add r5, r5, #PROC_INFO_SZ       @ sizeof(proc_info_list)
    2 b( y$ b/ _6 V2 P
  13.     168     cmp r5, r6' ]  c9 ^/ ?; m  s7 c" h0 {
  14.     169     blo 1b2 \/ l$ d8 Q: L& T( w
  15.     170     mov r5, #0              @ unknown processor
    1 L/ G& @5 U( _
  16.     171 2:  mov pc, lr( q6 _% h+ V% o' j' ]

  17. 0 o# `5 x8 X; C$ F2 w6 H6 q9 w0 j4 v
  18.     187     .long   __proc_info_begin/ P" U/ U+ s, L2 e+ `
  19.     188     .long   __proc_info_end! S: F, J* ~9 x9 x
  20.     189 3:  .long   .
    : Q) w( v$ v: k7 k( D' x5 y% E
  21.     190     .long   __arch_info_begin% G' j+ F$ v6 X; w0 B( M/ R' O
  22.     191     .long   __arch_info_end
複製代碼
跳了很多檔案,建議指令和object code如何link的概念要有,就會很清楚了。
3#
 樓主| 發表於 2008-10-13 17:20:35 | 只看該作者
我們從 head-common.S返回之後,接著繼續看。# x+ s1 ?1 a4 Y/ b" Y6 d
" B! |4 f) c, `) ?
line 84, movs的意思是說,做mov的動作,並且將指令的S bit設起來,最後的結果也會update CPSR。這個指令執行的過程當中,會根據你要搬動的值去把N and Z flag設好。這個是有助於下個指令做check的動作。* Q) Y5 c$ M6 N# u0 W! z
line 85, 就是r5 = 0的話,就跳到__error_p去執行。
* c( D$ `% w4 h6 {line 86, 跳到__lookup_machine_type。原理跟剛剛找proc的資料一樣。
  1.      83         bl      __lookup_processor_type         @ r5=procinfo r9=cpuid
    2 e2 l2 r" S* Q! K$ Q* M! G3 R8 \8 J
  2.      84         movs    r10, r5                         @ invalid processor (r5=0)?2 p/ ]) N0 [) L2 t, D3 T
  3.      85         beq     __error_p                       @ yes, error 'p'
    9 d# L; j1 \+ ~0 v" p& V" T
  4.      86         bl      __lookup_machine_type           @ r5=machinfo
複製代碼
看得出來跟proc很像,有個兩個小地方是
3 \0 w8 X, o, \
2 ?- k, K* g# z7 S( {# I% R6 h" y! q1. line 207,用ldmia不是用ldmda。原因就在於存放arch 和 proc info 的位址剛好相反。一個在lable3的上面。一個在的下方。設計上應該是可以做修改的。
, ?- p. r! }9 X7 e/ Q. m% d, o# W4 G! b: F
2. arch定義的方式是透過macro,可以先看 ./include/asm-arm/mach/arch.h。裡頭有個 MACHINE_START ,這邊會設好macro,到時候直接使用就可以把arch的info宣告好。 例如 ./arch/arm/mach-omap1/board-generic.c
  1. /* macro */9 @4 M( R) ]2 Z: h8 \7 s: C: C% E! @
  2.      50 #define MACHINE_START(_type,_name)                      \7 N( `. c+ T& r( A8 D3 \
  3.      51 static const struct machine_desc __mach_desc_##_type    \6 k5 O0 l* T/ a* F
  4.      52  __used                                                 \
    / l# q4 m6 j# n9 e( e
  5.      53  __attribute__((__section__(".arch.info.init"))) = {    \
    , i% E3 m/ ]  J# c1 \" }
  6.      54         .nr             = MACH_TYPE_##_type,            \3 J0 m$ V0 ^# o, U- p5 D( a* D
  7.      55         .name           = _name,5 h5 D! `* m) u8 V/ l/ v$ x
  8.      56# D4 i. Q0 F+ m4 c" Y% `" H
  9.      57 #define MACHINE_END                             \2 y( D+ _5 u, Y2 s" P
  10.      58 };. c# D' O& `7 z5 q( `& a# n' l- `2 S
  11.      /* 用法 */
    ; a7 w* y0 [) r$ D' x+ a
  12.      93 MACHINE_START(OMAP_GENERIC, "Generic OMAP1510/1610/1710")! @' h& C+ P7 R) @! B% A: F7 \
  13.      94         /* Maintainer: Tony Lindgren <tony@atomide.com> */, J4 y7 l& C  U; e
  14.      95         .phys_io        = 0xfff00000,7 H9 h. i' J6 b, C' t
  15.      96         .io_pg_offst    = ((0xfef00000) >> 18) & 0xfffc,
    6 s7 m0 T+ q( M$ V
  16.      97         .boot_params    = 0x10000100,
    ; K. h5 Q$ W% B7 H/ }, t+ n
  17.      98         .map_io         = omap_generic_map_io,5 _9 [4 C! V) D( {1 P
  18.      99         .init_irq       = omap_generic_init_irq,! Y4 c6 Z, w7 e+ G; T, h% p
  19.     100         .init_machine   = omap_generic_init,
    + ?& m2 r! ^# z$ H3 H1 C: u; c
  20.     101         .timer          = &omap_timer,
    / ~1 O+ a% q2 x
  21.     102 MACHINE_END5 s$ s7 [$ t( b+ p" ]

  22. 4 p+ @2 n4 E% C3 h2 \
  23.     /* func */
    & [8 [/ G( n3 E% I: `
  24.     204         .type   __lookup_machine_type, %function
    % s) H: [! h- v8 F7 n  o
  25.     205 __lookup_machine_type:
    $ f, D. v2 w6 ?) g+ ?
  26.     206         adr     r3, 3b+ ?9 p: F/ ]; i/ j) N0 p7 K- x2 P
  27.     207         ldmia   r3, {r4, r5, r6}
    6 a: J' |& U) ^) ]' e' ~4 Z; d
  28.     208         sub     r3, r3, r4                      @ get offset between virt&phys/ R' O; y% g: E* o4 y' J' [
  29.     209         add     r5, r5, r3                      @ convert virt addresses to
    1 m" d; R. L5 A' f) B5 ?7 I
  30.     210         add     r6, r6, r3                      @ physical address space+ e% v  L# o' N& m* v
  31.     211 1:      ldr     r3, [r5, #MACHINFO_TYPE]        @ get machine type
    , Y' ~  t6 ]7 V6 p
  32.     212         teq     r3, r1                          @ matches loader number?1 W: p0 F1 D3 Y' `' C
  33.     213         beq     2f                              @ found1 F3 C' Y8 ?- @! D0 m
  34.     214         add     r5, r5, #SIZEOF_MACHINE_DESC    @ next machine_desc0 ~3 Z+ P  _! i9 T, U/ N0 d; _
  35.     215         cmp     r5, r61 c4 S) O& b2 z! p# g% W, M
  36.     216         blo     1b) @  Q( ~7 E3 {/ E
  37.     217         mov     r5, #0                          @ unknown machine& _1 F5 C# w) L
  38.     218 2:      mov     pc, lr
複製代碼
4#
 樓主| 發表於 2008-10-13 17:56:46 | 只看該作者
接著我們又返回到head.S,
5 h0 E) \* n2 d2 `+ D2 T+ f' C& Nline 87~88也是做check動作。6 Q  J4 a; }' `3 F* O& U
line 89跳到vet_atags。在head-common.S
  1.      87         movs    r8, r5                          @ invalid machine (r5=0)?
    ) ?+ |, \) D3 n6 m
  2.      88         beq     __error_a                       @ yes, error 'a'
    1 I8 p8 p2 _! f# n2 M8 h
  3.      89         bl      __vet_atags
    ( F: w+ P/ ?, U8 k. h
  4.      90         bl      __create_page_tables
複製代碼
line 245, tst會去做and動作。並且update flags。這邊的用意是判斷位址是不是aligned。
. Y* }9 u' I5 b+ i; mline 246, 沒有aligned跳到label 1,就返回了。
3 J0 X, G4 Z0 g7 G- w1 @  {( }line 248~250, 讀取atags所在的address裡頭的值到r5,看看是否不等於ATAG_CORE_SIZE,不等於的話也是返回。7 I* Z+ b3 ?" q! a, I0 V: }
line 251~254, 判斷一下第一個達到的atag pointer是不是等於ATAG_CORE。如果正確的話,等一下要讀取atag的資料,才會正確。( q2 M# ~' z- B* ^1 Q* w4 M0 h" X
(atag是由bootloader帶給linux kernel的東西,用來告知booting所需要知道的參數。例如螢幕寬度,記憶體大小等等)
  1.      14 #define ATAG_CORE 0x54410001( i: L- F( `& a8 n2 m# J: ^
  2.      15 #define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)- l' p" E& U% H# U( P* y
  3. ( q0 e; O1 ?. V( W
  4.     243         .type   __vet_atags, %function
    ( y: N$ \  ~% d8 ~5 a; ?- z  J. ^# q
  5.     244 __vet_atags:$ G0 }! u1 s6 B+ v1 J; o
  6.     245         tst     r2, #0x3                        @ aligned?
    % r& R3 ?8 o) d# }8 _5 y4 n
  7.     246         bne     1f0 }: l0 ~" T+ g
  8.     247
    5 r, W" W* F# w  u1 G
  9.     248         ldr     r5, [r2, #0]                    @ is first tag ATAG_CORE?
    ' N- g4 `# q/ o' j" `2 v( Z0 E+ ]
  10.     249         subs    r5, r5, #ATAG_CORE_SIZE
    ! \5 c8 x4 U; z8 B1 [
  11.     250         bne     1f/ k/ Q, _# w: k: ?$ D% g( \
  12.     251         ldr     r5, [r2, #4]
    * r5 J1 t7 ^0 d
  13.     252         ldr     r6, =ATAG_CORE2 X# q6 `$ B6 ?8 c$ W/ N
  14.     253         cmp     r5, r6* K, H6 E+ `6 b& M+ H2 h4 u5 V0 _
  15.     254         bne     1f& y+ I! e7 h- ?% r$ }% E
  16.     255; O* O- W/ L- Y, i' h
  17.     256         mov     pc, lr                          @ atag pointer is ok
    , o$ R  F+ e; u. A! I
  18.     257
    2 v" s! p( ~4 l; @; d4 W7 _2 t
  19.     258 1:      mov     r2, #0: o: ]" g3 b" v: T" |; i
  20.     259         mov     pc, lr
複製代碼
接著我們又跳回去head.S。  
7 h5 L$ ?- e/ f( _% @4 v$ G' bline 90,又跳到 __create_page_tables。   (很累人....應該會死不少腦細胞)
$ ^/ l( ^, j1 u: D$ |哇!page table?!!如雷貫耳的東西,不知道會怎麼做。剛剛偷看了一下,@@還蠻長了,先到這邊好了,下次在繼續寫。
5#
 樓主| 發表於 2008-10-14 12:13:51 | 只看該作者
由於code看起來似乎越來越難解釋,會需要在檔案之間跳來跳去。建議一些基礎的東西可以再多複習幾次(其實是在說我自己 ),閱讀source code上收穫會比較多。例如:
: i9 Z0 J0 _2 e+ X; y3 v1 x7 M9 u9 ~7 d; o, e. e: n' V# F# r
1. arm instruction set - 這個最好每個指令的意思都大略看過一次,行有餘力多看幾個版本,armv4, v5 or v6。
' \! H$ G- V8 [* a; S9 C& ?2. compiler & assembler & linker - toolchain工具做的事情和功能,大致的流程和功能要有概念。; G' t) D4 k$ J" @
3. Makefile & link script - 這兩個功能和撰寫的方式要有簡單的概念。
) R7 l& h. g  z' w3 V7 E7 d+ J2 s2 C3 Y
以上1是非常重要的重點,2&3只要有大致上的概念就可以,因為trace code的時候,有時需要跳到Makeflie&Link script去看最後object code編排的位址。8 K$ b. }. f! t$ D  U

# H, e  [$ f( j3 L3 U$ H( G& u由於我們trace到了page table這個關鍵字,在開始之前,稍微簡短的解釋,為了幫助了解,儘量用易懂的概念講,有些用詞可能會和一些真實狀況不同,但是懂了之後,應該會有能力分辨,至於正式而學術上解說,很多書上應該都有,或是google一下就很多啦∼
6#
 樓主| 發表於 2008-10-14 12:14:47 | 只看該作者
page table本身是很抽象的東西,尤其是對所謂的 user-mode application programmer 來說,大部分的狀況它是不需要被考慮到的部份,但是他的產生卻對os和driver帶來了很多影響。我們從一個小小的疑問出發。
6 a$ g  a% g! D' y# p
  ~- U6 e/ ~  c0 ?『產生page table到底是要給誰用的?』% c' M1 }* g" F+ z0 c8 {$ g

- R7 ?, N( l$ d0 s; B/ B# x其實真正作用在它上面的H/W是MMU,一旦CPU啟用了MMU,當cpu嘗試去記憶體讀取一個operand的時候,cpu內部打出去的位址訊號都會先送到mmu,mmu會拿著這個位址去對照page table,看看這個位址是不是被轉換到另外一個位置(還會確認讀寫權力),最後才會到真正的位址去讀寫。) M, S6 O, T5 e5 D- d

  A) \0 b& E5 G! }7 p這樣來看CPU其實一開始打出去的位址訊號其實不是最後可以拿到資料的位址,我們稱為virtual address(va),mmu打出來的位址才能真正拿到資料,所以我們把mmu打出去的位址稱為physical address(pa)。那用來查詢這個位址對照關係的表格,就是page table。
3 R3 y7 k! o$ U
4 d; m; O# ^/ a! C到這邊我們有一個簡單的概念。來想像一個小問題,一個普通的周邊(例如你的顯示卡),因為他並不具有MMU功能,hw只看得懂pa,但是CPU卻是作用在va,假如我想去對hw的控制暫存器做讀寫,到底要用va還是pa?
7#
 樓主| 發表於 2008-10-14 12:15:51 | 只看該作者
這時,寫driver的人就必須要小心,設定給硬體看的位址必須要先轉成pa(像是設定DMA),給CPU執行的程式碼要使用va,這對一開始嘗試寫driver但是又不瞭解va pa之間的差別的人,常常產生很多疑問。
* P( b( h- |. h- e: L+ B
6 z4 d9 C$ ~% S5 q4 e3 L, M現在我們回頭想想OS,既然我們跑到create page table,可以預期的是他想要將MMU打開,因此希望預先建立起一個page table,讓MMU知道目前os想規劃的位址對應和讀取權力是如何被安排的。所以os必須考慮到現在的系統究竟是長怎樣?應該要如何被安排?是不是有那些要被保護?好吧∼因為我們完全對os不了解,也不知道該安排什麼,只能祈禱在trace code完後,可以找到這些問題的答案,或者發現一些沒想到的問題。
. e! e  j9 o6 a1 g) P7 ~0 @, Z' k+ e5 U9 R
知道了page table的大致上的功能,下篇就可以專心的研究這個table的長相,和它想規劃出的系統模樣。# ]0 b! q5 u) s+ ~2 D
: r7 @2 d# r7 k) F8 D. s+ Y3 S7 @
p.s. 字數限制好像變短了。   (看來很難寫)
8#
 樓主| 發表於 2008-10-14 13:56:17 | 只看該作者
由於字數限制挑整,用詞儘量精簡,以便可以貼必要source。另外,以後寫到一段落,考慮收集成一篇完整的文章,這樣應就不會因為用回覆的方式,造成閱讀斷斷續續的問題。希望有人對文章呈現方式有想法的話,可以跟我講。
9#
 樓主| 發表於 2008-10-14 13:57:39 | 只看該作者
現在,讓我們跳入create_page_tables吧∼
& E; T8 J% N! y! X# \8 E* {line 216,會跳到pgtbl的macro去執行,其實只是載入一個位址。
/ R3 o+ [! k2 H! k1 ~) w2 J) t/ y, ~7 Q
只是這個位址因為你硬體規劃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 */, x9 n% S! r9 \! T5 y: `# N3 d
  2.      95 textofs-y       := 0x00008000
    2 @- x: }5 P3 v7 d* {
  3.     152 TEXT_OFFSET := $(textofs-y)
複製代碼
  1. /* include/asm-arm/arch-omap/memory.h */
    : I9 m- X( n( X! B* y
  2.      40 #define PHYS_OFFSET             UL(0x10000000)
    7 O: v# X1 U7 U

  3. % F0 Q, d; N; l. t6 F7 S) g
  4.      /* arch/arm/kernel/head.S */% q& Q* b$ g/ _2 k6 T
  5.      29 #define KERNEL_RAM_VADDR        (PAGE_OFFSET + TEXT_OFFSET)& K  g" Q0 O, b$ U' r
  6.      30 #define KERNEL_RAM_PADDR        (PHYS_OFFSET + TEXT_OFFSET)9 T) c' h/ J8 G3 K
  7. ; J( H' X& X" y4 q* S( B7 Y/ M
  8.      47         .macro  pgtbl, rd6 {) e0 Y! R: s% J7 B, X7 R# _
  9.      48         ldr     \rd, =(KERNEL_RAM_PADDR - 0x4000)/ U7 ~3 i/ ~- `
  10.      49         .endm
    : D3 T7 X0 I6 U  A5 A! \3 X
  11. . d: R- _9 w/ ^
  12.     216         pgtbl   r4                              @ page table address
複製代碼
10#
 樓主| 發表於 2008-10-14 14:16:53 | 只看該作者
得到pg table的開始位置之後,當然就是初始化囉。1 w$ L+ A8 b# b$ r* h
line 221, 將pg table的base addr放到r0.8 t5 ^9 J6 o' K2 v6 J0 a( b( o0 T
line 223, 將pg table的end addr放到r6.
) K) N5 a8 @! A7 g% ~6 `" E; bline 224~228, 反覆地將0x0寫到pg table的區段裡頭,每個loop寫16bytes. (4x4),直到碰到end,結果就是把它全部clear成0x0.
  1.     221         mov     r0, r4
    0 y& J+ N7 B+ B( l" G8 n
  2.     222         mov     r3, #0
    1 v$ ]! i: ?2 K
  3.     223         add     r6, r0, #0x40008 ?9 W) \& D( B3 o/ J. J5 A6 ]: W
  4.     224 1:      str     r3, [r0], #44 F& a) `$ g) ^4 u' u2 e* {5 v
  5.     225         str     r3, [r0], #4* `% |) w0 k: r6 u. G
  6.     226         str     r3, [r0], #4/ }) \4 L% @8 ]& M1 D+ p
  7.     227         str     r3, [r0], #4
    : v+ v% ^# z; n$ X( F
  8.     228         teq     r0, r6
    2 M8 p0 Z3 @8 ?# j8 y' S& E
  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 | 只看該作者
問題怎麼填值??/ Q+ a5 F- R/ N; b  }
拿出ARM的手冊,翻到MMU章節。一看發現page有很多種,還得分first level和second level。
1 x9 E$ _! s1 _- A6 O/ a8 v# L8 g! b) a
念書時的印象,是從1st level在去查2nd level,先看怎麼設定1st level (不知以前老師有沒有亂教)
1 ^% j! X% O! g5 r4 L/ U9 E1. [31:20]存著section base addr
/ ^4 C: i% K' t" x; g$ U' H2. [19:2]存著mmu flags  |- C4 q# m  H! b  n' X
3. [1:0]用來辨別這是存放哪種page, 有四種:* V. h4 r6 O# }1 s! }
   a. fault (00) b. coarse page (01) c. section (1st level) (10) d. fine page (11)! p; `; n  x) i) o, w  B% `3 ?- A' [
4. pg tabel資料要存放到 [31:14] = translation base, [13:2] = table index , [1:0] = 0b00 的位址' L9 ~* ]6 C2 b- _

' J, a1 ~/ p3 i% m% h; G來看code是怎麼設定。
) K+ g* m4 ~  X5 u& c6 D( Z& x/ G1 L% d4 m$ J2 j0 j9 T: T) O
line 239, 將pc的值往右shift 20次放到r6.這是取得前20高位元的資料,有點像是 1.。! B" l2 _" {0 @9 V& [. X& e
line 240, 將r6往左shift 20次之後,和r7做or的動作,剛好也是 2.提到的mmu flags和1.處理好的資料做整理。
& }- ~3 G! u  L, C所以前面兩個做完,就完成了bit[31:2]。
9 h0 z1 v& H% j5 ~  F1 }9 u2 eline 241, 將r3的資料寫到r4+(r6<<0x2)的地方去,剛好是4.提到的位址。(lucky)
  1.     239         mov     r6, pc, lsr #20
    # L9 `! i4 q: T7 c- a: H
  2.     240         orr     r3, r7, r6, lsl #20
    1 B. K) L" ?  V! j$ L# Z( A
  3.     241         str     r3, [r4, r6, lsl #2]
複製代碼
p.s. 用pc值剛好可以算出當前的page。
12#
 樓主| 發表於 2008-10-22 19:47:03 | 只看該作者
最近又被釘上了,開始忙碌,大概沒太多時間貼文∼
* `( R/ i& c  ^4 d0 d6 _6 t
$ f, j: x0 y2 n4 F- i( t( F7 S上篇已經將pc所屬的page table entry(pte)設定好,接著繼續看
& m! y# K0 l7 C- M0 Iline 247, 248, 將KERNEL_START的位址往右shift 18算出pte的offset,(等於line239&241,239&241是先shift right 20然後shift left 2),並將剛剛r3的值設給pte。『!』的意思是會把address存到r0。
: S# k' J  X9 t9 V5 i$ z; N" y# k3 H* ]3 Q* B5 i" E; M/ {
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) >> 18) v! l6 X) F1 Q' j' `; D- m6 }
  2.     248         str     r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]! - y5 Q% O; H* v
  3.     249         ldr     r6, =(KERNEL_END - 1)
    / b: x$ H3 L! w* s6 y1 r
  4.     250         add     r0, r0, #4
    ( p# _4 N% X- \1 T" ]" e5 e. X
  5.     251         add     r6, r4, r6, lsr #189 o8 s9 N6 o+ k! d
  6.     252 1:      cmp     r0, r64 z; w, o# c8 L" m
  7.     253         add     r3, r3, #1 << 205 n  D+ _5 _# Z$ F* I. S
  8.     254         strls   r3, [r0], #41 e% X* {/ D& h
  9.     255         bls     1b
複製代碼
13#
 樓主| 發表於 2008-10-22 20:24:58 | 只看該作者
line279,PAGE_OFFSET是規範RAM mapping完後的virtual address,我們將這個位址所屬的pte算出來放到r0。( d  E8 X+ S* }8 E4 c/ G. s
line 280~283,將要 map 的physical address的方式算出來放到r6。
, o& _; }- K. B4 k: D9 Z4 Pline 284,最後將結果存到r0所指到的pte。
- a. S/ M; ^2 ?$ q" Y' C, P0 @
" N! c- e! b  p# I  `以上三個動作,就是做好 ram 起頭的位址一開始map,還沒做完整塊map。由於我們目前的設定方式每塊是1MB,所以這邊是將RAM開始的1MB做map。7 W$ {8 @( [. r) l5 I7 H3 r% `* x2 w
  z1 s; K" e- H- R' p
line 327,返回,結束一開始的create page table的動作,我們可以看出其實並沒有做完整的初始化page table,所以之後應該會有其他page table的細節。
  1.     279         add     r0, r4, #PAGE_OFFSET >> 18
    ! K7 T" u; O' v
  2.     280         orr     r6, r7, #(PHYS_OFFSET & 0xff000000)0 H5 \# u0 \; M) u
  3.     281         .if     (PHYS_OFFSET & 0x00f00000)( ~1 O7 |) U6 q  h9 ]4 m' e. T
  4.     282         orr     r6, r6, #(PHYS_OFFSET & 0x00f00000)
    & K0 P8 V( A  f! j" Y# k
  5.     283         .endif( o  j6 |9 ]5 t9 h
  6.     284         str     r6, [r0]
    * D' r1 k" j+ g0 g6 _) q6 D
  7.     327         mov     pc, lr
複製代碼
附帶一提,我們這邊省略了一些用ifdef包起來的程式碼,像是一開始會印一些output message的程式碼(line286~326)。
14#
 樓主| 發表於 2008-10-22 20:37:08 | 只看該作者
自create page table返回後,我們偷偷看一下接下來的程式碼,
9 ^/ l, B. W, m6 Jline 99, 將switch_data擺到r13
) f( @% n. d/ Z& W* W8 W0 X5 ]line 101, 將enable_mmu擺到lr
9 T1 R+ }- a- ^. ?+ }7 k3 Q+ I" a+ Lline 102, 將pc改跳到r10+PROCINFO_INITFUNC的地方去' \: p$ O9 V4 V- z. O
( O6 k. s* P+ q) c- t' b
其實這邊有點玄機,switch_data和enable_mmu都是function,結果位址都只是被存起來,並沒有直接跳過去執行。其實,雖然程式碼是順序switch_data->enable_mmu->proc init function,但其實執行的順序會是 procinfo 的init function-> enable_mmu -> switch_data 。至於,為什麼要這樣寫的原因?就不清楚了,也還沒仔細推敲過。
6 V: E6 q5 p" l5 ~' o) v+ a7 L% `7 ?. c3 U5 N
switch_data最後就會跳到大家都很熟悉的start_kernel(). 詳細要賣個關子,最近會忙一下,得要過一陣子才能貼文∼  
  1.      99         ldr     r13, __switch_data              @ address to jump to after
    , o3 H3 i( ?# x4 j& E. M
  2.     100                                                 @ mmu has been enabled4 [6 Z# c3 |& U0 O1 `
  3.     101         adr     lr, __enable_mmu                @ return (PIC) address
    2 [# U) G; n) w  @
  4.     102         add     pc, r10, #PROCINFO_INITFUNC
複製代碼
15#
 樓主| 發表於 2009-7-4 01:09:36 | 只看該作者
老店重新開張~
% r, c, b1 S! J" i1 ^& J& Z; `1 I& g1 x' Z2 l
花了一些時間把舊的貼文整理到一個blog
- _5 E" z: y. t/ [有把一些敘述修改過
0 T$ O! H$ g/ N# [8 E; n' e( B希望會比較容易集中閱讀6 g3 E: N0 r: o  I( a: o# R
目前因為某些敘述不容易' V! k( \' r4 M+ Q+ d0 V
還是比較偏向筆記式而且用字不夠精確
/ b1 T# V/ ?# K/ Y  l" t' S/ ~希望之後能夠慢慢有系統地整理
" O' S% `* n, j9 F( f% q' k大家有興趣的話
4 K6 s8 t4 _3 A4 M可以來看看和討論 ) p6 }4 q( x0 l! m
http://gogojesseco.blogspot.com/4 w. r2 ^3 i2 q0 p! V0 A7 q
% Z! n1 y" G- r8 g2 \
以後可能會採取  先在chip123貼新文章
# K: x+ G' ^/ {, n# m& h- ^& K, X+ a0 K. V慢慢整理到blog上的方式
1 u- r3 |1 q2 {4 ~1 m- i因為chip123比較方便討論 =)
$ N5 v+ }! ]( ?/ Yblog編輯修改起來比較方便
% Z  J# G: C6 l' W# M0 m: h閱讀也比較集中   大家可以在這邊看到討論
- I$ X6 S! ^4 l然後在blog看到完整的文章 (類似BBS精華區的感覺)

評分

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

查看全部評分

16#
 樓主| 發表於 2009-7-15 17:07:03 | 只看該作者
隔了很長一段時間沒update
1 U' C* e$ |0 ^! g, a之前程式碼走到 ./arch/arm/kernel/head.S 的 line 99 附近
  1.      99         ldr     r13, __switch_data              @ address to jump to after
    6 _! [9 B! }# C. z6 Z1 P! j
  2.     100                                                 @ mmu has been enabled. c) v  K* ?  X$ W' i5 Y' a, f
  3.     101         adr     lr, __enable_mmu                @ return (PIC) address
    $ i3 ]9 G) }- s* \* C' n- ~
  4.     102         add     pc, r10, #PROCINFO_INITFUNC
複製代碼
line 99, 將__switch_data放到r13。(留作之後用)1 {2 J4 H+ L) M; g0 S3 }
line 101, 將__enable_mmu的addr放到lr。(留作之後用)
! S- ~; A( z# J% w0 s/ ^, Tline 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" \' H7 \. U+ ]
  2. 374 __arm926_setup:2 G- H  R$ D# ^1 {( F6 L- j
  3. 375         mov     r0, #0- G6 m# a, g" B( ]* g+ K
  4. 376         mcr     p15, 0, r0, c7, c7              @ invalidate I,D caches on v4  z' a3 B8 Q7 V! n# D
  5. 377         mcr     p15, 0, r0, c7, c10, 4          @ drain write buffer on v4+ l5 f# ?& \9 C# L3 G2 o
  6. 378 #ifdef CONFIG_MMU/ d) Q8 n$ O; C
  7. 379         mcr     p15, 0, r0, c8, c7              @ invalidate I,D TLBs on v45 B/ u- h/ F6 N5 ?- |; z" A
  8. 380 #endif
    , h/ w; {" o; O5 u

  9. ' k3 |8 @* R$ f* B5 I
  10. 388         adr     r5, arm926_crval
    - \2 f) w! R2 z4 X; |3 k* |
  11. 389         ldmia   r5, {r5, r6}# ~9 {5 l- F, P- k0 g
  12. 390         mrc     p15, 0, r0, c1, c0              @ get control register v4" a" e: p, l1 e( z
  13. 391         bic     r0, r0, r5
    ! {" c5 C5 z% l5 e& |5 [
  14. 392         orr     r0, r0, r6
    5 G. E# s- l/ h* P# A, J
  15. 8 g4 X8 f6 r  r5 R
  16. 396         mov     pc, lr! k- c+ g8 {6 S7 r- c. X; L3 K" Z
  17. 397         .size   __arm926_setup, . - __arm926_setup
複製代碼
這邊的程式碼就跟CPU有很大的相依性,4 o+ P7 X8 w6 g
line 375~380, 主要就是invalidate CPU的I&D cache和清空write buffer。" a, ?" ?$ [/ x" o
line 388~392, 把cp15的設定讀出來,並且將一些預設狀態做好運算放到r0。(預設值從arm926_crval這邊可以取得。)# V+ G  O/ N3 z+ o7 e3 s
line 396, 直接把pc跳到lr,因為我們之前已經將lr = enable_mmu,所以直接跳過去。
17#
 樓主| 發表於 2009-7-15 17:29:45 | 只看該作者
  1. 155 __enable_mmu:. L, H* m: E. }# {9 t
  2. 170         mov     r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \. d+ R4 c5 F' h- s8 P5 u' b. g
  3. 171                       domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \0 n' E2 P1 @7 T1 Y) p
  4. 172                       domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
    " X" K( ^6 T  R* c& W
  5. 173                       domain_val(DOMAIN_IO, DOMAIN_CLIENT))6 T9 e8 f% s1 p( w6 B
  6. 174         mcr     p15, 0, r5, c3, c0, 0           @ load domain access register3 c0 s  V% r9 Z: a. [, O1 a2 T! G0 ?
  7. 175         mcr     p15, 0, r4, c2, c0, 0           @ load page table pointer
    7 I1 i/ t0 {3 p7 b* O
  8. 176         b       __turn_mmu_on- h5 n, o) h7 [
  9. 177 ENDPROC(__enable_mmu)
複製代碼
line 170~174,設置好domain access。(domain access可以先當成設置access的權限,或許以後可以寫詳細的文章說明)' R6 S+ v! M# r
line 175~176,將create好的page table開頭丟給mmu。跳到turn_mmu_on
  1. 191 __turn_mmu_on:
    ; C& D# N7 w3 x* G+ g! F1 ]5 ]
  2. 192         mov     r0, r06 P) E+ X! D$ u+ s' I; C. Q% }2 Q
  3. 193         mcr     p15, 0, r0, c1, c0, 0           @ write control reg
    , }4 d& w5 e+ B4 H7 g# e' ]: a
  4. 194         mrc     p15, 0, r3, c0, c0, 0           @ read id reg
    2 ^6 D) G; d; H8 j  ]  o
  5. 195         mov     r3, r3
    , Q' t) {2 q$ d) s" G; |& p' u8 D
  6. 196         mov     r3, r3
    + u+ g$ h( z" _$ k  O5 Y
  7. 197         mov     pc, r13' b4 A% U, A& y+ D+ e
  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:
    % C! w+ f. r' A5 o7 y! I, @
  2. 19         .long   __mmap_switched
    " B4 \8 v9 |. M& L0 F
  3. 20         .long   __data_loc                      @ r4$ P/ u: h1 q3 t+ U2 h
  4. 21         .long   _data                           @ r5
    - x! a% g1 [1 b
  5. 22         .long   __bss_start                     @ r6! g% ^6 F5 g; v
  6. 23         .long   _end                            @ r7: b6 u' b$ f& c+ E1 x
  7. 24         .long   processor_id                    @ r4; l9 g4 @5 T8 P1 ?
  8. 25         .long   __machine_arch_type             @ r51 L& |2 v7 g: l, ~
  9. 26         .long   __atags_pointer                 @ r6" d6 l# S7 m( u9 E
  10. 27         .long   cr_alignment                    @ r7! B) U: v0 l' ^: \" I0 Y
  11. 28         .long   init_thread_union + THREAD_START_SP @ sp  f0 g) M0 e* ?% A3 P
  12. 29) B0 r  I; _: S# M
複製代碼
switch_data的第一行就是 __mmap_switched,所以我們直接看line 39。
18#
 樓主| 發表於 2009-7-15 17:30:00 | 只看該作者
  1. 39 __mmap_switched:
    ) n  }2 f3 N, V; r. v: Y" n8 n
  2. 40         adr     r3, __switch_data + 4
    3 H, c) Y" y4 c$ z2 s$ G; v  V  w7 D
  3. 41& E, o. c9 c: b- O% S: f6 u
  4. 42         ldmia   r3!, {r4, r5, r6, r7}
    7 U; m+ h3 s" x9 ]
  5. 43         cmp     r4, r5                          @ Copy data segment if needed
    4 |, p- q5 }  W4 h
  6. 44 1:      cmpne   r5, r6( {/ |! K! k( x3 g4 J* W
  7. 45         ldrne   fp, [r4], #4' f) Y; \: n  M
  8. 46         strne   fp, [r5], #4, c9 d$ S, a1 }8 ]+ N5 x  S1 |
  9. 47         bne     1b% B+ A/ }! k1 ~5 t- ?5 d
  10. 48( N% t8 r4 F! ?0 c3 D/ {' G
  11. 49         mov     fp, #0                          @ Clear BSS (and zero fp)
    0 q/ y$ ]* S" q& g2 R
  12. 50 1:      cmp     r6, r7! A1 ]; ?7 j* M
  13. 51         strcc   fp, [r6],#4
    2 Q! E: Q# _' L7 b# S, ^6 k. I" z
  14. 52         bcc     1b
    8 i2 K# e4 @2 x4 H% E8 g0 Y
  15. 53
    ! G/ `. r1 a* h1 E: e+ g
  16. 54         ldmia   r3, {r4, r5, r6, r7, sp}5 E3 a7 O8 ?9 S
  17. 55         str     r9, [r4]                        @ Save processor ID/ G; h4 z0 I% j0 b9 C3 |* |
  18. 56         str     r1, [r5]                        @ Save machine type
    * G) f/ Q- s1 R3 n
  19. 57         str     r2, [r6]                        @ Save atags pointer
    % R0 \  J+ h: b; _: Y& h
  20. 58         bic     r4, r0, #CR_A                   @ Clear 'A' bit2 P' k1 r8 q5 t3 k6 d5 \0 a% M
  21. 59         stmia   r7, {r0, r4}                    @ Save control register values
      O1 B5 d( K4 u& ]7 m- L7 l& P
  22. 60         b       start_kernel
    - h0 \0 x, A" }6 r+ u# l9 P" j
  23. 61 ENDPROC(__mmap_switched)
複製代碼
switch_data的第一行就是 __mmap_switched,所以我們直接看line 39。# t, x$ u- R8 L0 w# T
line 39,將__data_loc的addr放到r3( a+ t2 i* Z' \4 f' Z: [
line 42,從r3的位址,連續讀取四筆資料到r4, r5, r6, r7
7 q. P  @1 s% \line 43~47,看看data segment是不是需要搬動。
" J4 x9 I* d7 J& ?# W0 s; sline 49~52, clear BSS。' e$ w. k. }- b. A  T

6 Q- S# u1 u# f8 T' _- Y% _3 i由於linux kernel在進入start_kernel前有一些前提必須要滿足:
' A$ Q2 i, t2 K- ?. t6 ~# Er0  = cp#15 control register
5 l% H: n" X' N% T) D& Ir1  = machine ID
2 A! G+ v# ^. W  J  b( Sr2  = atags pointer- h3 T1 ?; L4 A# ~
r9  = processor ID7 d4 W6 |( {* q" [- ?' S+ Q# |

% p8 A5 Z& {! G5 d, L# @所以line 54~59就是在做這些準備。3 v: r5 T) m' m- R4 ]3 H
最後呢? 我們就跳到start_kernel了。(而且還是用b start_kernel,表示我們不會再回來了)
( @& w7 V. j9 [( T! y8 |. c0 |6 |0 ^7 r
看一下start_kernel()在./init/main.c,終於跳出architecture specific的目錄,表示, X) x" f* J( W5 Y- `+ J% |3 {! h. i
我們真正的開始linux kernel的初始化。
7 H8 K- r0 J+ U! p1 f. A像是 shedule init, console init, memory init, irq init等等都在start_kernel裡頭。- q- K) u5 b$ l
到這邊之後,應該就可以深入linux kernel中,各項比較跟hardware不那麼相關的軟體部分。

評分

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

查看全部評分

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

本版積分規則

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

GMT+8, 2024-6-6 05:52 AM , Processed in 0.147518 second(s), 19 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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