<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設計應用 > 嵌入式 arm平臺kernel啟動第一階段匯編head.s分析

          嵌入式 arm平臺kernel啟動第一階段匯編head.s分析

          作者: 時間:2016-11-09 來源:網絡 收藏
          arm_linux內核生成過程:

          1.依據arch/arm/kernel/vmlinux.lds生成linux內核源碼根目錄下的vmlinux,這個vmlinux屬于未壓縮,帶調試信息、符號表的最初的內核,大小約23MB;
          命令:arm-linux-gnu-ld-ovmlinux-Tarch/arm/kernel/vmlinux.lds
          arch/arm/kernel/head.o
          init/built-in.o
          --start-group
          arch/arm/mach-s3c2410/built-in.o
          kernel/built-in.o
          mm/built-in.o
          fs/built-in.o
          ipc/built-in.o
          drivers/built-in.o
          net/built-in.o
          --end-group.tmp_kallsyms2.o

          本文引用地址:http://www.ex-cimer.com/article/201611/317681.htm


          2.將上面的vmlinux去除調試信息、注釋、符號表等內容,生成arch/arm/boot/Image,這是不帶多余信息的linux內核,Image的大小約3.2MB;
          命令:arm-linux-gnu-objcopy-Obinary-Svmlinuxarch/arm/boot/Image

          3.將arch/arm/boot/Image用gzip-9壓縮生成arch/arm/boot/compressed/piggy.gz大小約1.5MB;命令:gzip-f-9arch/arm/boot/compressed/piggy.gz

          4.編譯arch/arm/boot/compressed/piggy.S生成arch/arm/boot/compressed/piggy.o大小約1.5MB,這里實際上是將piggy.gz通過piggy.S編譯進piggy.o文件中。而piggy.S文件僅有6行,只是包含了文件piggy.gz;
          命令:arm-linux-gnu-gcc-oarch/arm/boot/compressed/piggy.oarch/arm/boot/compressed/piggy.S


          5.依據arch/arm/boot/compressed/vmlinux.lds將arch/arm/boot/compressed/目錄下的文件head.o、piggy.o、misc.o鏈接生成arch/arm/boot/compressed/vmlinux,這個vmlinux是經過壓縮且含有自解壓代碼的內核,大小約1.5MB;
          命令:arm-linux-gnu-ldzreladdr=0x30008000params_phys=0x30000100-Tarch/arm/boot/compressed/vmlinux.ldsarch/arm/boot/compressed/head.oarch/arm/boot/compressed/piggy.oarch/arm/boot/compressed/misc.o-oarch/arm/boot/compressed/vmlinux


          6.將arch/arm/boot/compressed/vmlinux去除調試信息、注釋、符號表等內容,生成arch/arm/boot/zImage大小約1.5MB;這已經是一個可以使用的linux內核映像文件了;
          命令:arm-linux-gnu-objcopy-Obinary-Sarch/arm/boot/compressed/vmlinuxarch/arm/boot/zImage


          7.將arch/arm/boot/zImage添加64Bytes的相關信息打包為arch/arm/boot/uImage大小約1.5MB;
          命令:./mkimage-Aarm-Olinux-Tkernel-Cnone-a0x30008000-e0x30008000-nLinux-2.6.35.7-darch/arm/boot/zImagearch/arm/boot/uImage

          內核啟動分析:

          本文著重分析S3C2410linux-2.6.35.7內核啟動的詳細過程,主要包括:zImage解壓縮階段、vmlinux啟動匯編階段、startkernel到創(chuàng)建第一個進程階段三個部分,一般將其稱為linux內核啟動一、二、三階段,本文也將采用這種表達方式。對于zImage之前的啟動過程,本文不做表述,可參考前面正亮講得“u-boot的啟動過程分析”。

          本文中涉及到的術語約定如下:

          基本內核映像:即內核編譯過程中最終在內核源代碼根目錄下生成的vmlinux映像文件,并不包含任何內核解壓縮和重定位代碼;

          zImage內核映像:包含了內核piggy.o及解壓縮和重定位代碼,通常是目標板bootloader加載的對象;

          zImage下載地址:即bootloader將zImage下載到目標板內存的某個地址或者nandread將zImage讀到內存的某個地址;

          zImage加載地址:由Linux的bootloader完成的將zImage搬移到目標板內存的某個位置所對應的地址值,默認值0x30008000。

          1、Linux內核啟動第一階段:內核解壓縮和重定位

          該階段是從u-boot引導進入內核執(zhí)行的第一階段,我們知道u-boot引導內核啟動的最后一步是:通過一個函數(shù)指針thekernel()帶三個參數(shù)跳轉到內核(zImage)入口點開始執(zhí)行,此時,u-boot的任務已經完成,控制權完全交給內核(zImage)。

          稍作解釋,在u-boot的文件archarmlibbootm.c(uboot-2010.9)中定義了thekernel,并在do_bootm_linux的最后執(zhí)行thekernel.

          定義如下:void(*theKernel)(intzero,intarch,uintparams);

          theKernel=(void(*)(int,int,uint))ntohl(hdr->ih_ep);

          //hdr->ih_ep----EntryPointAddressuImage中指定的內核入口點,這里是0x30008000。

          theKernel(0,bd->bi_arch_number,bd->bi_boot_params);

          其中第二個參數(shù)為機器ID,第三參數(shù)為u-boot傳遞給內核參數(shù)存放在內存中的首地址,此處是0x30000100。

          由上述zImage的生成過程我們可以知道,第一階段運行的內核映像實際就是arch/arm/boot/compressed/vmlinux,而這一階段所涉及的文件也只有三個:

          (1)arch/arm/boot/compressed/vmlinux.lds

          (2)arch/arm/boot/compressed/head.S

          (3)arch/arm/boot/compressed/misc.c

          下面的圖是使用64MRAM時,通常的內存分布圖:

          下面我們的分析集中在arch/arm/boot/compressed/head.S,適當參考vmlinux.lds。

          從linux/arch/arm/boot/compressed/vmlinux.lds文件可以看出head.S的入口地址為ENTRY(_start),也就是head.S匯編文件的_start標號開始的第一條指令。

          下面從head.S中得_start標號開始分析。(有些指令不影響初始化,暫時略去不分析)

          代碼位置在/arch/arm/boot/compressed/head.S中:

          start:

          .typestart,#function/*uboot跳轉到內核后執(zhí)行的第一條代碼*/

          .rept8/*重復定義8次下面的指令,也就是空出中斷向量表的位置*/

          movr0,r0/*就是nop指令*/

          .endr

          b1f@跳轉到后面的標號1處

          .word0x016f2818@輔助引導程序的幻數(shù),用來判斷鏡像是否是zImage

          .wordstart@加載運行zImage的絕對地址,start表示賦的初值

          .word_edata@zImage結尾地址,_edata是在vmlinux.lds.S中定義的,表示init,text,data三個段的結束位置

          1:movr7,r1@savearchitectureID保存體系結構ID用r1保存

          movr8,r2@saveatagspointer保存r2寄存器參數(shù)列表,r0始終為0

          mrsr2,cpsr@getcurrentmode得到當前模式

          tstr2,#3@notuser?,tst實際上是相與,判斷是否處于用戶模式

          bnenot_angel@如果不是處于用戶模式,就跳轉到not_angel標號處

          /*如果是普通用戶模式,則通過軟中斷進入超級用戶權限模式*/

          movr0,#0x17@angel_SWIreason_EnterSVC,向SWI中傳遞參數(shù)

          swi0x123456@angel_SWI_ARM這個是讓用戶空間進入SVC空間

          not_angel:/*表示非用戶模式,可以直接關閉中斷*/

          mrsr2,cpsr@turnoffinterruptsto讀出cpsr寄存器的值放到r2中

          orrr2,r2,#0xc0@preventangelfromrunning關閉中斷

          msrcpsr_c,r2@把r2的值從新寫回到cpsr中

          /*讀入地址表。因為我們的代碼可以在任何地址執(zhí)行,也就是位置無關代碼(PIC),所以我們需要加上一個偏移量。下面有每一個列表項的具體意義。

          LC0是表的首項,它本身就是在此head.s中定義的

          .typeLC0,#object

          LC0:.wordLC0@r1LC0表的起始位置

          .word__bss_start@r2bss段的起始地址在vmlinux.lds.S中定義

          .word_end@r3zImage(bss)連接的結束地址在vmlinux.lds.S中定義

          .wordzreladdr@r4zImage的連接地址,我們在arch/arm/mach-s3c2410/makefile.boot中定義的

          .word_start@r5zImage的基地址,bootp/init.S中的_start函數(shù),主要起傳遞參數(shù)作用

          .word_got_start@r6GOT(全局偏移表)起始地址,_got_start是在compressed/vmlinux.lds.in中定義的

          .word_got_end@ipGOT結束地址

          .worduser_stack+4096@sp用戶棧底user_stack是緊跟在bss段的后面的,在compressed/vmlinux.lds.in中定義的

          @在本head.S的末尾定義了zImag的臨時??臻g,在這里分配了4K的空間用來做堆棧。

          .section".stack","w"

          user_stack:.space4096

          GOT表的初值是連接器指定的,當時程序并不知道代碼在哪個地址執(zhí)行。如果當前運行的地址已經和表上的地址不一樣,還要修正GOT表。*/

          .text

          adrr0,LC0/*把地址表的起始地址放入r0中*/

          ldmiar0,{r1,r2,r3,r4,r5,r6,ip,sp}/*加載地址表中的所有地址到相應的寄存器*/

          @r0是運行時地址,而r1則是鏈接時地址,而它們兩都是表示LC0表的起始位置,這樣他們兩的差則是運行和鏈接的偏移量,糾正了這個偏移量才可以運行與”地址相關的代碼“

          subsr0,r0,r1@calculatethedeltaoffset計算偏移量,并放入r0中

          beqnot_relocated@ifdeltaiszero,wearerunningattheaddresswewerelinkedat.

          @如果為0,則不用重定位了,直接跳轉到標號not_relocated處執(zhí)行

          /*

          *偏移量不為零,說明運行在不同的地址,那么需要修正幾個指針

          *r5–zImage基地址

          *r6–GOT(全局偏移表)起始地址

          *ip–GOT結束地址

          */

          addr5,r5,r0/*加上偏移量修正zImage基地址*/

          addr6,r6,r0/*加上偏移量修正GOT(全局偏移表)起始地址*/

          addip,ip,r0/*加上偏移量修正GOT(全局偏移表)結束地址*/

          /*

          *這時需要修正BSS區(qū)域的指針,我們平臺適用。

          *r2–BSS起始地址

          *r3–BSS結束地址

          *sp–堆棧指針

          */

          addr2,r2,r0/*加上偏移量修正BSS起始地址*/

          addr3,r3,r0/*加上偏移量修正BSS結束地址*/

          addsp,sp,r0/*加上偏移量修正堆棧指針*/

          /*

          *重新定位GOT表中所有的項.

          */

          1:ldrr1,[r6,#0]@relocateentriesintheGOT

          addr1,r1,r0@table.Thisfixesupthe

          strr1,[r6],#4@Creferences.

          cmpr6,ip

          blo1b

          not_relocated:movr0,#0

          1:strr0,[r2],#4@clearbss清除bss段

          strr0,[r2],#4

          strr0,[r2],#4

          strr0,[r2],#4

          cmpr2,r3

          blo1b

          blcache_on/*開啟指令和數(shù)據Cache,為了加快解壓速度*/

          @這里的r1,r2之間的空間為解壓縮內核程序所使用,也是傳遞給decompress_kernel的第二和第三的參數(shù)

          movr1,sp@mallocspaceabovestack

          addr2,sp,#0x10000@64kmax解壓縮的緩沖區(qū)

          @下面程序的意義就是保證解壓地址和當前程序的地址不重疊。上面分配了64KB的空間來做解壓時的數(shù)據緩存。

          /*

          *檢查是否會覆蓋內核映像本身

          *r4=最終解壓后的內核首地址

          *r5=zImage的運行時首地址,一般為0x30008000

          *r2=endofmallocspace分配空間的結束地址(并且處于本映像的前面)

          *基本要求:r4>=r2或者r4+映像長度<=r5

          (1)vmlinux的起始地址大于zImage運行時所需的最大地址(r2),那么直接將zImage解壓到vmlinux的目標地址

          cmpr4,r2

          bhswont_overwrite/*如果r4大于或等于r2的話*/

          (2)zImage的起始地址大于vmlinux的目標起始地址加上vmlinux大?。?M)的地址,所以將zImage直接解壓到vmlinux的目標地址

          addr0,r4,#4096*1024@4MBlargestkernelsize

          cmpr0,r5

          blswont_overwrite/*如果r4+映像長度<=r5的話*/

          @前兩種方案通常都不成立,不會跳轉到wont_overwrite標號處,會繼續(xù)走如下分支,其解壓后的內存分配示意圖如下:

          movr5,r2@decompressaftermallocspace

          movr0,r5/*解壓程序從分配空間后面存放*/

          movr3,r7

          bldecompress_kernel

          /進入decompress_kernel*/

          @decompress_kernel共有4個參數(shù),解壓的內核地址、緩存區(qū)首地址、緩存區(qū)尾地址、和芯片ID,返回解壓縮代碼的長度。

          decompress_kernel(ulgoutput_start,ulgfree_mem_ptr_p,ulgfree_mem_ptr_end_p,

          intarch_id)

          {

          output_data=(uch*)output_start;/*Pointstokernelstart*/

          free_mem_ptr=free_mem_ptr_p;/*保存緩存區(qū)首地址*/

          free_mem_ptr_end=free_mem_ptr_end_p;/*保存緩沖區(qū)結束地址*/

          __machine_arch_type=arch_id;

          arch_decomp_setup();

          makecrc();/*鏡像校驗*/

          putstr("UncompressingLinux...");

          gunzip();/*通過free_mem_ptr來解壓縮*/

          putstr("done,bootingthekernel.n");

          returnoutput_ptr;/*返回鏡像的大小*/

          }

          /從decompress_kernel函數(shù)返回*/

          addr0,r0,#127+128

          bicr0,r0,#127@alignthekernellength對齊內核長度

          /*

          *r0=解壓后內核長度

          *r1-r3=未使用

          *r4=真正內核執(zhí)行地址0x30008000

          *r5=臨時解壓內核Image的起始地址

          *r6=處理器ID

          *r7=體系結構ID

          *r8=參數(shù)列表0x30000100

          *r9-r14=未使用

          */

          @完成了解壓縮之后,由于內核沒有解壓到正確的地址,最后必須通過代碼搬移來搬到指定的地址0x30008000。搬運過程中有

          @可能會覆蓋掉現(xiàn)在運行的重定位代碼,所以必須將這段代碼搬運到安全的地方,

          @這里搬運到的地址是解壓縮了的代碼的后面r5+r0的位置。

          addr1,r5,r0@endofdecompressedkernel解壓內核的結束地址

          adrr2,reloc_start

          ldrr3,LC1@LC1:.wordreloc_end-reloc_start表示reloc_start段代碼的大小

          addr3,r2,r3

          1:ldmiar2!,{r9-r14}@copyrelocationcode

          stmiar1!,{r9-r14}

          ldmiar2!,{r9-r14}

          stmiar1!,{r9-r14}

          cmpr2,r3

          blo1b

          blcache_clean_flush@清cache

          ARM(addpc,r5,r0)@callrelocationcode跳轉到重定位代碼開始執(zhí)行

          @在此處會調用重定位代碼reloc_start來將Image的代碼從緩沖區(qū)r5幫運到最終的目的地r4:0x30008000處

          reloc_start:addr9,r5,r0@r9中存放的是臨時解壓內核的末尾地址

          subr9,r9,#128@不拷貝堆棧

          movr1,r4@r1中存放的是目的地址0x30008000

          1:

          .rept4

          ldmiar5!,{r0,r2,r3,r10-r14}@relocatekernel

          stmiar1!,{r0,r2,r3,r10-r14}/*搬運內核Image的過程*/

          .endr

          cmpr5,r9

          blo1b

          movsp,r1/*留出堆棧的位置*/

          addsp,sp,#128@relocatethestack

          call_kernel:blcache_clean_flush@清除cache

          blcache_off@關閉cache

          movr0,#0@mustbezero

          movr1,r7@restorearchitecturenumber

          movr2,r8@restoreatagspointer

          @這里就是最終我們從zImage跳轉到Image的偉大一跳了,跳之前準備好r0,r1,r2

          movpc,r4@callkernel

          到此kernel的第一階段zImage解壓縮階段已經執(zhí)行完。

          第二階段的在另外一篇中分析。



          評論


          技術專區(qū)

          關閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();