28、(7)Linux內核引導階段之 __switch_data(切換數據)


 

完成的工作復制數據段、清除BSS段、設置棧指針、保存CPU ID到processor_id變量、保存機器類型ID到__machine_arch_type變量、調用start_kernel

 

在 arch/arm/kernel/head-common.S 中:

 

00014:  .type __switch_data, %object   
00015: __switch_data:
00016:  .long __mmap_switched   
00017:  .long __data_loc @ r4     //數據存放位置
00018:  .long __data_start @ r5   //數據開始位置
00019:  .long __bss_start @ r6    //bbs開始位置,也是數據結束位置
00020:  .long _end @ r7           
00021:  .long processor_id @ r4
00022:  .long __machine_arch_type @ r5
00023:  .long cr_alignment @ r6
00024:  .long init_thread_union + THREAD_START_SP @ sp
00025: 
00026: /*
00027:  * The following fragment of code is executed with the MMU on         * in MMU mode,
00028:  * and uses absolute addresses; this is not position independe      

* nt.
00029:  *
00030:  *  r0  = cp#15 control register
00031:  *  r1  = machine ID
00032:  *  r9  = processor ID
00033:  */
00034:  .type __mmap_switched, %function
00035: __mmap_switched:
00036:  adr r3, __switch_data + 4
00037: 
00038:  ldmia r3!, {r4, r5, r6, r7}      //復制數據段
00039:  cmp r4, r5
00040: 1: cmpne r5, r6
00041:  ldrne fp, [r4], #4
00042:  strne fp, [r5], #4
00043:  bne 1b
00044: 
00045:  mov fp, #0                      //清除BSS段
00046: 1: cmp r6, r7
00047:  strcc fp, [r6],#4               //cc:無符號小於
00048:  bcc 1b
00049: 
00050:  ldmia r3, {r4, r5, r6, sp}
00051:  str r9, [r4] @ Save processor ID
00052:  str r1, [r5] @ Save machine type
00053:  bic r4, r0, #CR_A @ Clear 'A' bit
00054:  stmia r6, {r0, r4} @ Save control register values
00055:  b start_kernel  

 

解析:

第14, 15行: 函數聲明
第16 - 24行: 定義了一些地址,例如第16行存儲的是 __mmap_switched 的地址, 第17行存儲的是 __data_loc 的地址 ......
第34, 35行: 函數 __mmap_switched
第36行: 取 __switch_data + 4的地址到r3. 從上文可以看到這個地址就是第17行的地址.
第37行: 依次取出從第17行到第20行的地址,存儲到r4, r5, r6, r7 中. 並
且累加r3的值.當執行完后, r3指向了第21行的位置. 
    對照上文,我們可以得知: 
                r4 - __data_loc (數據存放的位置)
                r5 - __data_start (數據開始的位置)
                r6 - __bss_start (bss開始的位置,也是數據結束位置)
                r7 - _end (bss結束的位置, 也是內核結束的位置)
 這幾個符號都是在 arch/arm/kernel/vmlinux.lds.S 中定義的變量:

        00102: #ifdef CONFIG_XIP_KERNEL
        00103:  __data_loc = ALIGN(4); /* location in binary */
        00104:  . = PAGE_OFFSET + TEXT_OFFSET;
        00105: #else
        00106:  . = ALIGN(THREAD_SIZE);
        00107:  __data_loc = .;
        00108: #endif
        00109: 
        00110:  .data : AT(__data_loc) {
        00111:  __data_start = .; /* address in memory */
        00112: 
        00113:  /*
        00114:   * first, the init task union, aligned
        00115:   * to an 8192 byte boundary.
        00116:   */
        00117:  *(.init.task)
        
                ......
                
        00158:  .bss : {
        00159:  __bss_start = .; /* BSS */
        00160:  *(.bss)
        00161:  *(COMMON)
        00162:  _end = .;
        00163:  }
        
   對於這四個變量,我們簡單的介紹一下:
        __data_loc   是數據存放的位置
        __data_start 是數據開始的位置
        __bss_start  是bss開始的位置, 也是數據結束位置
        _end         是bss結束的位置, 也是內核結束的位置
        
        其中對第110行的指令講解一下: 這里定義了.data 段,后面的AT(__data_loc) 的意思是這部分的內容是在__data_loc中存儲的(要注意,儲存的位置和鏈接的位置是可以不相同的).
        關於 AT 詳細的信息請參考 ld.info
        
        
        
        
第38行: 比較 __data_loc 和 __data_start
第39 - 43行: 這幾行是判斷數據存儲的位置和數據的開始的位置是否相等,如
果不相等,則需要搬運數據,從 __data_loc 將數據搬

             到 __data_start. 其中 __bss_start 是bss的開始的位置,也標志了 data 結束的位置,因而用其作為判斷數據是否

             搬運完成.
第45 - 48行: 是清除 bss 段的內容,將其都置成0. 這里使用 _end 來判
斷 bss 的結束位置.
第50行: 因為在第38行的時候,r3被更新到指向第21行的位置.因而這里取得

r4, r5, r6, sp的值分別是:
                r4 - processor_id
                r5 - __machine_arch_type
                r6 - cr_alignment
                sp - init_thread_union + THREAD_START_SP

           processor_id 和 __machine_arch_type 這兩個變量是
在 arch/arm/kernel/setup.c 中 第62, 63行中定義的.
        cr_alignment 是在 arch/arm/kernel/entry-armv.S 中定義的:

        00182:  .globl cr_alignment
        00183:  .globl cr_no_alignment
        00184: cr_alignment:
        00185:  .space 4
        00186: cr_no_alignment:
        00187:  .space 4
        
        init_thread_union 是 init進程的基地
址. 在 arch/arm/kernel/init_task.c 中:

        00033: union thread_union init_thread_union
        00034:  __attribute__((__section__(".init.task"))) =
        00035:  { INIT_THREAD_INFO(init_task) };         

    對照 vmlnux.lds.S 中的 的117行,我們可以知道init task是存放   
在 .data 段的開始8k, 並且是THREAD_SIZE(8k)對齊的
第51行: 將r9中存放的 processor id (在arch/arm/kernel/head.S 75
行) 賦值給變量 processor_id
第52行: 將r1中存放的 machine id (見"啟動條件"一節)賦值給變量 
machine_arch_type
第53行: 清除r0中的 CR_A 位並將值存到r4中. CR_A 是
在 include/asm-arm/system.h 21行定義, 是cp15控制寄存器c1的Bit[1]

        (alignment fault enable/disable)
第54行: 這一行是存儲控制寄存器的值.  從上面 arch/arm/kernel/entry-armv.S 的代碼我們可以得知.
        這一句是將r0存儲到了 cr_alignment 中,將r4存儲到
了 cr_no_alignment 中.
第55行: 最終跳轉到start_kernel    


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com