隨筆之GoldFish Kernel啓動過程當中arm彙編分析

隨筆之GoldFish Kernel啓動過程當中arm彙編分析

  分析

電子版下載http://download.csdn.net/detail/innost/4834459 linux

本節介紹Kernel啓動。此時Piggy已經將vimlinux解壓,BL將執行權限傳給了Kernel編程

代碼在arch/arm/kernel/head.S中。相關代碼以下: vim

//將採用C/C++註釋語句 數據結構


/*

   .section是GNU ASM的語法。格式以下:

    .section name[,"flags"[,@type]]   其中,name是必須的,flags是可選。

    "ax"表示:a爲section is allocatable,x爲executable。

*/

   .section ".text.head", "ax"
//這個ENTRY(stext)有至關的含義。在kernel/vmlinux.ld.S中,也定義了一個ENTRY。在ld

//語法中,ENTRY是一個command,用來定義入口點。因此,這裏就是kernel執行的入口點函數。

ENTRY(stext)

   /*

     MSR:是ARM彙編指令,用來將數據copy到status register寄存器中。cpsr_c表示要操做

     CPSR寄存器的Control標誌。

   */

  

    msr    cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode

                        @ and irqs disabled

1.1  MSR設置I/FCPU Mode

CPSR全稱是Current Process Status Register,用來表示當前CPU的狀態,也可用於控制。相關控制位如圖1所示: 架構

1 CPSR控制位 dom

由圖1可知: ide

  • q  N/Z/C/V控制位用來表示負/零/進位/溢出,屬於User Flags,便可在UserMode下操做。A
  • q  I/F表示Interrupt和Fast Interrupt使能位。
  • q  Mode用來控制CPU當前的模式。ARM CPU一共有7種模式。

根據上面的代碼,首先將禁止I/F中斷,並進入Supervisor模式,也就是OS運行的模式。圖2ARM CPU支持的CPU模式。 函數

2  ARM CPU支持的運行模式 測試

另外,MSR指令操做的格式以下: ui

3  MSR指令格式

其中最重要的是fields,目前支持:

  • q  c:設置control bit。對應位爲16。
  • q  x:設置extension bit。對應位爲17。
  • q  s:設置status bit。對應位爲18。
  • q  f:設置flags field。對應位爲19。

4 MSR二進制格式

直接看上面的解釋,還不是很清楚,由於設置的是MSR指令自己的內容,具體對應到CPSR呢,則可經過下面的僞語句獲得:

5 MSR 設置說明

從代碼可知:

msr    cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE

//上面代碼將設置CPSR0到第7位,恰好是控制I/F和設置CPU模式的。

1.2  ARM CP15協處理器控制

設置好CPU模式後,下面的工做就是獲取CPU的信息。在ARM中,協處理(coprocessor15中用於管理CPU信息和MMU相關的工做。CP15也是ARM中最重要的處理器,之後會常常碰到。

先看下面這條語句:

mrc    p15, 0, r9, c0, c0        @ get processor id

MRC是ARM指令,用來從協處理對應的寄存器讀取信息到CPU的寄存器,對應寫協處理寄存器的指令是MCR。兩者的語法格式(注意,是操做CP15的時候)如圖6所示:

6  MRC操做CP15的格式說明

  • q  Rd:本指令用得是R9,也就是協處理的信息會保存到R9中。
  • q  CRn:MRC中協處理器的主要寄存器。此處用得是C0。標準寫法是C0,C1一直到C15。
  • q  CRm:附屬信息。若是沒有附屬信息,則使用C0。
  • q  opcode2,相似附屬信息。根據CRn來決定是否須要。若是不指定,則使用0。

CP15有很重要的做用,可經過操做CP15的寄存器來控制它。如圖7所示:

7  CP15各個寄存器的做用

先來看此處操做的C0寄存器。

opcode2在指令中默認是0,因此將取出Main ID register的信息。

獲得的結果將怎麼使用呢?來看下一句指令:

bl    __lookup_processor_type        @ r5=procinfo r9=cpuid

BLARM中的跳轉指令,至關於調用函數吧。__lookup_processor_type用來獲得CPU信息。注意,這個函數調用的參數是R9R9的值是從CP15 C0寄存器讀取出來的,而是是Main ID。下面看看此函數如何處理R9

1.3  __lookup_procesoor_type分析

該函數在head-Common.S中定義。下面逐行分析它,這裏會碰到幾個重要的指令及其用法。

__lookup_processor_type:

    //adr是一條僞指令,其做用是將3f標籤的地址賦給R3。這個僞指令實際上是可拆分紅多條指令

   //因爲後面的3f是相對當前PC位置而言,因此R3實際上存儲的是3f的物理地址。

    adr    r3, 3f//f是forward之意。標誌3在此代碼以後聲明

    /*

   ldm是load multiple register的意思,它的做用是將[r3]對應的內存內容存儲到r5,r6,

    r7寄存器中。DA是Decrease After的意思。ARM彙編在這裏有4種模式,DA,IA,DB,IB等

    此處的ldmda,將把3F所在的內容依序傳遞給R7,R6,R5。每傳遞一次,R3遞減4個字節。

    */

    ldmda    r3, {r5 - r7}

上面語句執行完後:

q  R5=__proc_info_begin,這個值是虛地址。

q  R6=__proc_info_end。

q  R7=.。

以上幾個值都是虛地址。__proc_info_begin/endld在連接時候指定的信息。

8  arc/arm/kernel/vmlinx.lds.S文件

從中能夠看出,__proc_info_begin/end包含了代碼中定義在.proc.info.init段的內容。如圖9所示。

9  proc-V7.s定義的proc.info.init的內容

爲何是proc -v7.S文件呢,由於goldfish編譯的就是這個文件。從圖9能夠看出,其實也就是定義了一個數據結構罷了。

接着來看代碼

//r3指向3f的物理地址,r7指向虛擬地址,而如今只能訪問物理地址,因此須要找到一個offset

    sub    r3, r3, r7        @ get offset between virt&phys

    add    r5, r5, r3        @ convert virt addresses to

    add    r6, r6, r3        @ physical address space

通過上面的換算,r5,r6如今都指向__proc_info_begin/end的物理地址了。

//ldmia將[r5]的內存信息存儲到r3,r4中,每完成一次傳輸,r5自動加4.

1:  ldmia  r5, {r3, r4}        @ value, mask

    //下面將測試R9和mask以後的值是不是咱們想要的r3的值。根據圖9。應該是0x000f0000。

   //在Main ID register中,這代表[16-19]位是都是1.

    and    r4, r4, r9        @ mask wanted bits

    teq    r3, r4

    beq    2f   //若是是咱們想要的數據,則跳轉到2f

    //不然跳過一個PROC_INFO_SIZE,繼續找,通常只有一個PROC_INFO結構體。

    add    r5, r5, #PROC_INFO_SZ    @ sizeof(proc_info_list)

    cmp    r5, r6

    blo    1b

   //若是沒找到,則設置R5寄存器爲0

    mov    r5, #0            @ unknown processor

2:  mov    pc, lr   //從函數返回

ENDPROC(__lookup_processor_type)

 

/*

 * 提供一個C接口的lookup_process_type函數

 */

ENTRY(lookup_processor_type)

    stmfd    sp!, {r4 - r7, r9, lr}

    mov    r9, r0

    bl    __lookup_processor_type

    mov    r0, r5

    ldmfd    sp!, {r4 - r7, r9, pc}

ENDPROC(lookup_processor_type)

 

    .long    __proc_info_begin

    .long    __proc_info_end

3:    .long    .

    .long    __arch_info_begin

    .long    __arch_info_end

lookup_process_type其實比較簡單,這裏就再也不多說。但圖9的內容之後還要回過頭來繼續介紹。那裏將初始化CPU MMU相關的內容。

//若是r5爲空,則表示CPU信息獲取是否,調用__error_p,退出整個啓動

    movs    r10, r5                @ invalid processor (r5=0)?

    beq    __error_p            @ yes, error 'p'

不然,將調用__lookup_machine_type獲取機器信息。

1.4  __lookup_machine_type分析

該函數也是在head-comm.S中定義的。

__lookup_machine_type:

    adr    r3, 3b  //b是backward的意思。標誌3在此代碼以前聲明。

    //r4,r5,r6分別指向 label 3,__arch_info_begin和__arch_info_end

    ldmia    r3, {r4, r5, r6} 

    sub    r3, r3, r4

    add    r5, r5, r3

    add    r6, r6, r3

   //以上將獲得__arch_info_begin/end的物理地址

 

1:  ldr    r3, [r5, #MACHINFO_TYPE]

    //比較r1和MACHINFO_TYPE是否是一致。注意,r1的值是BL傳遞給它的

    teq    r3, r1                @ matches loader number?

    beq    2f                @ found

    add    r5, r5, #SIZEOF_MACHINE_DESC    @ next machine_desc

    cmp    r5, r6

    blo    1b

    mov    r5, #0                @ unknown machine

2:  mov    pc, lr

ENDPROC(__lookup_machine_type)

這裏涉及到另外一個關鍵數據結構,也就是定義在.arch.info.init段中的。如圖10所示:

10  .arch.info.init

從圖10可知,這個段其實對應了一個數據結構,即machine_desc.在咱們的goldfish平臺中,它是這麼定義的:

[arch/arm/mach-goldfish/board-goldfish.c]

//還須要加上:

   nr = MACH_TYPE_GOLDFISH

   name = "Goldfish"

MACHINE_START(GOLDFISH, "Goldfish")

    .phys_io    = IO_START,

    .io_pg_offst    = ((IO_BASE) >> 18) & 0xfffc,

    .boot_params    = 0x00000100,

    .map_io        = goldfish_map_io,

    .init_irq    = goldfish_init_irq,

    .init_machine    = goldfish_init,

    .timer        = &goldfish_timer,

MACHINE_END

完整的machine_desc定義如圖11所示:

11 machine_desc定義

Goldfish中,nr1441。詳情可參考arch/arm/tools/machine-types.h

另外,在BootLoader調用kernel以前,傳遞參數狀況如圖12所示:

12  arch/arm/boot/head.S調用kernel前傳遞參數

從圖12可知:

  • q  r1保存的是machine nr。

這部分代碼屬於BootLoader,至關複雜。之後再細說。

假設__lookup_machine_type一切正常

bl    __lookup_machine_type        @ r5=machinfo

    movs    r8, r5                @ invalid machine (r5=0)?

    beq    __error_a            @ yes, error 'a'

1.5  __vet_atags分析

接下來的任務就是Kernel校驗BL傳遞的啓動參數了。這部份內容和BootLoader有較大關係。

  

bl    __vet_atags

此處的核心概念就是ATAG_CORE/END之類的,由BootLoader往Kernel傳遞參數,主要是tag結構體

在arch/arm/include/asm/setup.h中。BL傳遞的是struct tag的鏈表,該鏈表以ATAG_CORE開頭,以ATAG_NONE結尾。

#define ATAG_CORE    0x54410001

#define ATAG_NONE    0x00000000

struct tag_header {

    __u32 size;

    __u32 tag;

};

struct tag {

  struct tag_header hdr;  //首先是一個頭,根據頭部的tag來判斷下面的union是哪一個

  union {

    struct tag_core        core;

    struct tag_mem32    mem;

    struct tag_videotext    videotext;

    struct tag_ramdisk    ramdisk;

    struct tag_initrd    initrd;

    struct tag_serialnr    serialnr;

    struct tag_revision    revision;

    struct tag_videolfb    videolfb;

    struct tag_cmdline    cmdline;

    struct tag_acorn    acorn;

    struct tag_memclk    memclk;

  } u;

};

你能夠根據上面的信息自行分析__vet_atags函數。

1.6  __create_page_tables分析

下面的任務就是調用__create_page_tables建立page table

  

bl    __create_page_tables  //調用__create_page_tables函數

此函數就在head.S中定義,代碼以下:

__create_page_tables:

    /*

       pgtbl是head.S中定義的一個宏,見下面的分析

    */

     pgtbl    r4

pgtbl定義了一個宏,相關代碼以下:

//TEXT_OFFSET是kernel鏡像在內存中的偏移量。通常定義爲0X8000,即32KB處

//PHYS_OFFSET:是內核鏡像在內存中的起始物理地址。上面兩者之和就是內核鏡像在機器上的

//物理地址。Goldfish平臺中,PHYS_OFFSET爲0。

//PAGE_OFFSET是Kernel鏡像在虛擬內存的起始地址,通常是3G處

#define KERNEL_RAM_VADDR    (PAGE_OFFSET + TEXT_OFFSET)

#define KERNEL_RAM_PADDR    (PHYS_OFFSET + TEXT_OFFSET)

.macro    pgtbl, rd //此宏調用完畢後,r4的值就是0x4000,即16KB

    ldr    \rd, =(KERNEL_RAM_PADDR - 0x4000)

    .endm

接着看代碼。

    mov    r0, r4

    mov    r3, #0

    add    r6, r0, #0x4000

    //STR將寄存器的值往內存中傳送。r3爲0,故內存的值被設置爲0.每調用一次str,r0遞增4

    //r0是base address,其值可自動增減。由arm address mode格式控制

1:  str    r3, [r0], #4

    str    r3, [r0], #4

    str    r3, [r0], #4

    str    r3, [r0], #4

    teq    r0, r6

    bne    1b //此循環調用完畢後,0x4000-0x8000的內存都被設置爲0。此時r0=32KB

 

    //r10存儲的是圖9中proc_info的第三個long,也就是mmuflas,用於設置MMU參數

    ldr    r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags

1.6.1  ARM MMU設置介紹

雖然上面最後一條語句是一個簡單的ldr,但背後的內容卻至關豐富,不把它搞清楚,後面的內容將解釋不清。來看proc-V7mm_mmuflags對應的值是什麼

.long   PMD_TYPE_SECT | \   // #define PMD_TYPE_SECT (2 << 0)

        PMD_SECT_BUFFERABLE | \ //#define PMD_SECT_BUFFERABLE (1 << 2)

        PMD_SECT_CACHEABLE | \//#define PMD_SECT_CACHEABLE (1 << 3)

        PMD_SECT_AP_WRITE | \//#define PMD_SECT_AP_WRITE (1 << 10)

        PMD_SECT_AP_READ //#define PMD_SECT_AP_READ (1 << 11)

上面代碼中把對應PMD_SECT_XXX的值顯示出來,可知它無非是定義了一個32位的常量,某些位置的值爲1,某些位置的值爲0。爲何要怎麼作呢?先來看ARM MMU所支持的虛實地址轉換機制。

13  ARM MMU虛實地址轉換

由圖13可知:

  • q  虛地址VA的[20-31]位和CP15 CR2的[14-31]位共同構成First Level地址。
  • q  從First Level地址將獲得一個First Level Descripter,也就是圖13中標明memory access的內容。
  • q  FLD中不一樣字段代表其內容是段尋址仍是頁尋址。主要是根據前2位來判斷。若是前2位是0b10則是段尋址。

結合圖13和前面的代碼:

  • q  PMD_TYPE_SECT = 2<<0,恰好就是0b10
  • q  C|B控制Cachable和Buffable的,對應爲[2,3]位
  • q  AP對應爲Access Point,對應爲[10,11]位。

另外,DomainARM CPU的一個重要概念,主要和權限有關。之後碰到再說。

至此,當ldr r7 xx執行完後,r7的值包含了section base address對應的[0-12]位的值。而section base address自己卻尚未賦值。

接下來的代碼就是爲了構造一個FLD的值。根據圖13section base address應該是[20-31]

   

//r6的值爲當前PC值右移20位

    mov    r6, pc, lsr #20

    orr    r3, r7, r6, lsl #20        @ flags + kernel base

    //此時,r3的值就是一個基於段尋址的FLD。把它存起來。位置是r4+r6<<2

    str    r3, [r4, r6, lsl #2]

如今r6存儲的是段尋址的基地址,須要把這個值存儲到對應表的位置,因爲在表中,每一項是4個字節,因此這裏須要乘以4,也就是lsl #2

稍微解釋下這裏左移4的緣由:

  • 1 r4存儲的是表的起始地址
  • 2 r6存儲的是offset
  • 3 r3存儲的是往r4[offset]的值
  • 4 因爲1個offset其實是4個字節,因此真實存儲的位置就是r4[4*offset] = r3

1.6.2  設置頁表

當理解上面代碼後,下面就是把kernel虛擬地址的位置存儲到r4表中了

繼續看代碼

  

//當即數的計算比較難理解,網上也沒有相關說法。不過,只要知道下面這段代碼就是存儲kernel

  //虛擬地址到對應頁表位置便可

   add    r0, r4,  #(KERNEL_START & 0xff000000) >> 18

   str    r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!

    ldr    r6, =(KERNEL_END - 1)

    add    r0, r0, #4  //r0 = ro+4

    add    r6, r4, r6, lsr #18 //r6=r4+r6>>18

1:    cmp    r0, r6

    add    r3, r3, #1 << 20 //r3 += 1<<20,每次遞增1M

    //ls是condition code,表示小於等於,即只要r0<=r6,strls就會執行

    strls    r3, [r0], #4

    bls    1b

 

   //map物理地址前1M到對應位置

   add    r0, r4, #PAGE_OFFSET >> 18

    orr    r6, r7, #(PHYS_OFFSET & 0xff000000)

    .if    (PHYS_OFFSET & 0x00f00000)

    orr    r6, r6, #(PHYS_OFFSET & 0x00f00000)

    .endif

    str    r6, [r0]

 

    mov    pc, lr

ENDPROC(__create_page_tables)

    .ltorg

1.6.3 總結

建議你們仔細體會create_page_tables這段內容。雖然之後不太可能會使用它們,但把這段代碼搞清楚仍是一個比較有意思的過程。

1.7  剩餘工做

回到head.S,最後還剩下幾句代碼:

//將__switch_data的位置存儲到r13

 ldr r13, __switch_data 

 //獲取__enable_mmu標籤的地址,並保存到lr中

 adr lr, __enable_mmu 

 //r10存儲的是__v7_proc_info的地址,#PROCINFO_INITFUNC是一個偏移量

 //執行完下條語句後,pc指向__v7_proc_info的b __v7_setup,故下面這條語句就是

 //執行__v7_setup函數

 add pc, r10, #PROCINFO_INITFUNC

ENDPROC(stext)

1.7.1  __switch_data說明

__switch_data標籤以下,主要存儲了一些數據。

[head-common.S]

.type __switch_data, %object

__switch_data:

 .long __mmap_switched

 .long __data_loc   @ r4

 .long _data    @ r5

 .long __bss_start   @ r6

 .long _end    @ r7

 .long processor_id   @ r4

 .long __machine_arch_type  @ r5

 .long __atags_pointer   @ r6

 .long cr_alignment   @ r7

 .long init_thread_union + THREAD_START_SP @ sp

之後再討論具體做用。

1.7.2  __v7_setup

先來看

add pc, r10, #PROCINFO_INITFUNC

實際上就是執行__v7_setup函數。代碼在mm/proc-v7.S中。

adr r12, __v7_setup_stack  @ the local stack

 stmia r12, {r0-r5, r7, r9, r11, lr}

 bl v7_flush_dcache_all

 ldmia r12, {r0-r5, r7, r9, r11, lr}

 mov r10, #0

dsb

#ifdef CONFIG_MMU  //goldfish定義了這個配置項

 mcr p15, 0, r10, c8, c7, 0  @ invalidate I + D TLBs

 mcr p15, 0, r10, c2, c0, 2  @ TTB control register

 orr r4, r4, #TTB_FLAGS

 mcr p15, 0, r4, c2, c0, 1  @ load TTB1

 mov r10, #0x1f   @ domains 0, 1 = manager

 mcr p15, 0, r10, c3, c0, 0  @ load domain access register

#endif

 ldr r5, =0xff0aa1a8

 ldr r6, =0x40e040e0

 mcr p15, 0, r5, c10, c2, 0  @ write PRRR

 mcr p15, 0, r6, c10, c2, 1  @ write NMRR

 adr r5, v7_crval

 ldmia r5, {r5, r6}

    mrc p15, 0, r0, c1, c0, 0  @ read control register

 bic r0, r0, r5   @ clear bits them

 orr r0, r0, r6   @ set them

//最後一句,將lr賦值給pc。執行完後,將跳到__enable_mmu函數。

 mov pc, lr    @ return to head.S:__ret

ENDPROC(__v7_setup)

上面代碼大可能是執行ARM v7 CPUMMU相關設置的,而其中的彙編語句到比較簡單。這也是ARM MMU設置的核心內容。下面咱們將結合ARM CPU Rerference簡單介紹下這些設置的內容。

請務必從ARM官方網頁上下載下面兩個文檔:

  • q  DDI0344D_cortex_a8_r2p1_trm.pdf:介紹CORTEX A8相關內容
  • q  DDI0406B_arm_architecture_reference_manual_errata_markup_10_0:最新的ARM架構參考手冊
1.     如何看懂MMU設置並掌握理論知識

如下面這個設置爲例:

mcr p15, 0, r10, c8, c7, 0

打開參考文檔DDI0344D_cortex_a8_r2p1_trm.pdf的第112頁。從這一頁開始,C15協處理器的各個寄存器的配置都有詳細的說明。如圖14所示

14 C8寄存器的設置

上圖中,左邊空白區域對應的是C8。可知,c8,c7,0的組合對應的是Invalidate unified TLB unlocked entries.詳細說明在page3-99

若是在此文檔中碰到有不理解的內容,就須要參考DDI0406B_arm_architecture_reference_manual_errata_markup_10_0。該文檔會介紹一些理論知識。

篇幅緣由,我就不在這裏囉嗦。已經告訴你們如何釣魚了,請你們本身嘗試!

1.7.3  __enable_mmu介紹

__v7_setup最後已經的mov pc, lr將使得CPU跳轉到__enable_mmu處,其代碼以下所示:

__enable_mmu:

#ifdef CONFIG_ALIGNMENT_TRAP

 orr r0, r0, #CR_A

#else

 bic r0, r0, #CR_A

#endif

#ifdef CONFIG_CPU_DCACHE_DISABLE

 bic r0, r0, #CR_C

#endif

#ifdef CONFIG_CPU_BPREDICT_DISABLE

 bic r0, r0, #CR_Z

#endif

#ifdef CONFIG_CPU_ICACHE_DISABLE

 bic r0, r0, #CR_I

#endif

//設置domain的權限,請參考前面的書籍瞭解DOMAIN在ARM MMU中的意義

mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \

        domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \

        domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \

        domain_val(DOMAIN_IO, DOMAIN_CLIENT))

//請參考前面的方法,瞭解下面這兩條語句的實際做用

 mcr p15, 0, r5, c3, c0, 0  @ load domain access register

 mcr p15, 0, r4, c2, c0, 0  @ load page table pointer

 b __turn_mmu_on //跳轉到__turn_mmu_on

ENDPROC(__enable_mmu)

簡單看看__turn_mmu_on:

__turn_mmu_on:

 mov r0, r0 //相似nop的空指令,浪費一點CPU時間,怕引發race condition發生

//c1,c0這兩個控制MMU的設置

 mcr p15, 0, r0, c1, c0, 0  @ write control reg

 mrc p15, 0, r3, c0, c0, 0  @ read id reg

 mov r3, r3

 mov r3, r3

//此時,MMU就正式啓動了

 mov pc, r13 //r13指向__switch_data

ENDPROC(__turn_mmu_on)

MMU啓動後,咱們也無需管什麼物理地址仍是虛擬地址,直接去看對應地址的代碼便可。若是您非對這個轉換過程很感興趣,建議您把那兩個參考書好好瞅瞅。

1.7.4  __mmaped_switched介紹

__switch_data第一個定義的就是__mmaped_switchedPC將執行這裏的指令:

__mmap_switched:

 adr r3, __switch_data + 4

 

 ldmia r3!, {r4, r5, r6, r7}

 cmp r4, r5    @ Copy data segment if needed

1: cmpne r5, r6

 ldrne fp, [r4], #4

 strne fp, [r5], #4

 bne 1b

 

 mov fp, #0    @ Clear BSS (and zero fp)

1: cmp r6, r7

 strcc fp, [r6],#4

 bcc 1b

 

 ldmia r3, {r4, r5, r6, r7, sp}

 str r9, [r4]   @ Save processor ID

 str r1, [r5]   @ Save machine type

 str r2, [r6]   @ Save atags pointer

 bic r4, r0, #CR_A   @ Clear 'A' bit

 stmia r7, {r0, r4}   @ Save control register values
//上面我就懶得廢話了,下面這句代碼相信各位都很瞭解。執行start_kernel函數。
 b start_kernel
ENDPROC(__mmap_switched)

 

二 總結

我以爲須要說明下爲何寫這篇文章:

早在20107月的時候,我就看了那本鼎鼎大名的《ARM體系結構與編程》,這應該是第一本系統介紹ARM體系結構和編程的書。可是沒看懂,全是枯燥的ARM CPU設置,純教科書。

最近由於工做的緣由,想把ARM這塊從新撿起來,想起2年的痛苦,以爲應該換個思路。ARM也好,彙編也好,咱們應該關注它的目的,而不是具體它是怎麼實現的。即瞭解What to do比了解How to do更重要(僅我我的目的而言,前者重要。不過在某些追求細節的時候,後者重要。須要你本身去判斷)。根據這個思路,我選擇以Linux Kernel啓動爲分析對象,大體研究流程以下:

  • q  先花幾天時間瞭解下ARM彙編的大概語句。
  • q  直接上代碼分析。不過你得對Kernel啓動的流程稍有了解。還好我在《深刻理解Android卷I》寫完後,花了點時間把這塊整理了下。請參考http://blog.csdn.net/innost/article/details/6693731
  • q  碰到不懂的彙編語句,就查參考手冊。這些還只是針對一些沒有背景知識的語句。
  • q  當碰到相似CP15操做的語句時,其背後每每包含了較多的CPU相關的知識,這時候就須要查閱前面提到的兩本參考書籍,去真真正正瞭解ARM CPU運行的相關原理。

大概通過2周先痛苦掙扎,到後面豁然開朗的過程,後續的研究就很是很是流暢了。

相關文章
相關標籤/搜索