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

          新聞中心

          arm BOOT閱讀筆記

          作者: 時(shí)間:2016-11-28 來源:網(wǎng)絡(luò) 收藏
          做了近兩年ARM下的驅(qū)動(dòng)開發(fā),常用的各個(gè)設(shè)備驅(qū)動(dòng)基本都碰過,不過Boot由于任務(wù)安排的緣故(公司一直有專人在做),一直沒有機(jī)會(huì)接觸,從剛開始接觸嵌入式的時(shí)候,就一直想弄清楚板子上電后,程序是怎么執(zhí)行的,不過看了下公司boot源碼,就很快放棄了,當(dāng)時(shí)對(duì)匯編充滿了畏懼,做了1年多的驅(qū)動(dòng)后,再看匯編感覺就沒那邊痛苦了,最近把boot的資料整理下,把我覺得boot比較核心的部分,完整的看了一遍,現(xiàn)在做個(gè)記號(hào).我把我覺得我之前比較困惑的難點(diǎn)整理出來,也許大家一起討論下,也許和我一樣的新手就可以少走些彎路.
          BOOT的核心就是relocate,目前見到的典型嵌入式系統(tǒng),除了處理器,至少都有ROM(norflash,nandflash)RAM(SDRAM),一般把Bootloader代碼放在norflash里面,而nandflash因?yàn)楸旧碛布虿荒茈S機(jī)訪問,一般只是用來放應(yīng)用程序.在系統(tǒng)加電或復(fù)位后,CPU通常由CPU制造商預(yù)先安排上地址取指令,arm體系下一般都是0x0地址取它的第一條指令,即PC = 0開始.
          和boot緊密相關(guān)的個(gè)人覺得就是一下幾點(diǎn).
          1.remap.
          remap比較簡單,和MMU的功能可以看做是等價(jià)的,只是一般remap地址估定為0x0 ,網(wǎng)上有個(gè)帖子叫<>專門講了它對(duì)remap的理解,對(duì)remap的作用是這樣講的: 當(dāng)ARM處理器上電或者Reset之后,處理器從0x0取指。因此,必須保證系統(tǒng)上電時(shí),0x0處有指令可以執(zhí)行。所以,上電的時(shí)候,0x0地址處必定是ROM或者Flash(NOR)。 但是,為了加快啟動(dòng)的速度,也方便可以更改異常向量表,加快中斷響應(yīng)速度,往往把異常向量表映射到更快、更寬(32bit/16bit)的RAM中。但是異常向量表的開始地址是由ARM架構(gòu)決定的,必須位于0x0處,因此,必須把RAM映射到0x0。
          文中提到了ARM處理器remap的三種情況,如下
          1)如果處理器有專門的寄存器可以完成Remap。那么Remap是通過Remap寄存器的相應(yīng)bit置1完成的。
          如Atmel AT91xx
          2)如果處理器沒有專門的寄存器,但是memory的bank控制寄存器可以用來配置bank的起始地址,那么只要把RAM的起始地址編程為0x0,也可以完成remap。如samsung s3c4510 .
          3)如果上面兩種機(jī)制都沒有,那么Remap就不要做了。因?yàn)樘幚砥鲗?shí)現(xiàn)決定了SDRAM對(duì)應(yīng)的bank地址是不能改變的。如Samsung S3c2410.
          不過我的看法有點(diǎn)稍微不一樣,如果上面兩種機(jī)制都沒有,那么Remap就不要做了,它給的典型例子是Samsung S3c2410 ,2410雖然sdram對(duì)應(yīng)的bank地址不能改變,但它有MMU功能, MMU可以起到remap的作用,常用的最典型的應(yīng)該是例子Samsung S3c44b0,它既沒有mmu,又SDRAM對(duì)應(yīng)地址有沒辦法改變.順便補(bǔ)充下除了4510可以改變每個(gè)bank的地址,還有華邦的w90P740(arm7),呵呵,我現(xiàn)在用的U就是這款U,可以把bank的地址隨意的設(shè)置.
          2.relocate .
          relocate (地址重定位),個(gè)人覺得這個(gè)是boot里面最麻煩也是最核心的部分,剛開始看boot代碼的時(shí)候,它簡直是我的惡夢,不知道大家分析boot的源碼流程是否這樣,也可能我大學(xué)不是計(jì)算機(jī)的,沒學(xué)過編譯原理(現(xiàn)在也沒看過)對(duì)鏈接和加載一無所知,有兩個(gè)星期非常痛苦,就是不懂人家boot里面的鏈接腳本為什么要那樣寫.網(wǎng)上關(guān)于uboot的帖子很多,但對(duì)鏈接加載這塊,始終寫的不詳細(xì),不知道是不是太過于基礎(chǔ)了,高手都不愿意講,最后自己找資料,發(fā)現(xiàn)其實(shí)一切痛苦的根源都是對(duì)鏈接和加載不太清楚造成的,但個(gè)人感覺boot除了初始化以外就是搬運(yùn)程序,如何搬運(yùn)?為什么要那樣搬運(yùn)都需要對(duì)硬件板的地址分布很清楚?而這些都是鏈接決定的,所以非弄清楚不可!
          1.我們?yōu)槭裁葱枰猺elocate ? 經(jīng)濟(jì)方面,(nandflash和norflash 每兆價(jià)格相差懸殊),把boot代碼放在norflash里面(為什么不放在nandflash里面,因?yàn)閚andflash讀需要驅(qū)動(dòng)支持,norflash可以直接訪問),boot通常很小,只需要占用幾十k的空間,所以只需要很小的norflash芯片,這樣很便宜,而把應(yīng)用程序通常很大,所以用價(jià)格低廉nandflash來儲(chǔ)存,實(shí)際應(yīng)用,通過執(zhí)行boot程序,把nandflash里面代碼和數(shù)據(jù)搬運(yùn)到內(nèi)存中來執(zhí)行,這樣比程序直接放在norflash里執(zhí)行,可以.另外還有運(yùn)行速度方面的差別,程序在norflash里執(zhí)行的速度遠(yuǎn)遠(yuǎn)小于在sdram中執(zhí)行的速度,為了追求更高的速度,也需要relocate,讓程序在sdram里面執(zhí)行 .
          2.關(guān)于加載域(VMA)和運(yùn)行域(LMA),杜春雷在它那本經(jīng)典的<>一書專門有一章來講加載域和運(yùn)行域不一致的情況,但我當(dāng)初接觸了它的這些加載域和運(yùn)行域后,看uboot的lds ,uboot的lds沒有設(shè)置LMA,只是設(shè)置了VMA,為此我疑惑很久.直到耐心的看了那本鏈接器和加載器的書才豁然明白( http://bbs.chinaunix.net/viewthread.php?tid=817770 ),任何一個(gè)鏈接器和加載器的基本工作都非常簡單: 將更抽象的名字與更底層的名字綁定起來,好讓程序員使用更抽象的名字編寫代碼,鏈接器的就是把源文件進(jìn)行符號(hào)解析,把解析出來的符號(hào)和地址的進(jìn)行綁定,把全局變量,函數(shù),標(biāo)號(hào)等等這些符合和地址綁定起來.
          3.boot上電后開始能夠正確執(zhí)行還有個(gè)很重要的原因,是要保證boot在系統(tǒng)加電或復(fù)位后最初執(zhí)行的代碼是跟地址無關(guān)的,(即在代碼搬運(yùn)前所執(zhí)行的代碼是與地址無關(guān)),地址無關(guān)即地址無關(guān)代碼生成的這個(gè)映象文件可以被放在內(nèi)存中的任何一個(gè)地址上運(yùn)行。對(duì)于地址無關(guān)的代碼, 尋址是基于pc值的, 在pc值上+/-一個(gè)偏移值, 得到運(yùn)行地址,如跳轉(zhuǎn)指令B.當(dāng)我們執(zhí)行完代碼搬運(yùn),就需要跳到和地址相關(guān)的地方去執(zhí)行,即我們的RAM中,一般是跳轉(zhuǎn)到一個(gè)標(biāo)號(hào), 這時(shí)地址相關(guān)代碼就開始運(yùn)行了ldr pc,_start_armboot.因?yàn)樵赽in映象生成的時(shí)候,就已經(jīng)把_start_armboot這個(gè)符號(hào),和實(shí)際地址綁定在一起,當(dāng)我們執(zhí)行l(wèi)dr pc,_start_armboot 程序就從在ROM中執(zhí)行跳入到RAM中了,但前提是我們進(jìn)行了代碼搬移,如果沒有代碼搬運(yùn)ldr pc,_start_armboot,RAM中沒有代碼程序就馬上飛掉了,所有我們?cè)谠诎徇\(yùn)之前不能尋址絕對(duì)地址有關(guān)代碼,必須執(zhí)行代碼地址無關(guān).
          拿u-boot-1.1.4下的smdk2410來做例子,和smdk2410 board密切相關(guān)的就兩個(gè)文件夾boardsmdk2410和cpuarm920t,里面核心文件就u-boot.lds , config.mk ,start.S .
          ENTRY(_start)
          SECTIONS
          {
          . = 0x00000000;//從0地址起始
          . = ALIGN(4);
          .text :
          {
          cpu/arm920t/start.o (.text)
          *(.text)
          }
          . = ALIGN(4);
          .rodata : { *(.rodata) }
          . = ALIGN(4);
          .data : { *(.data) }
          . = ALIGN(4);
          .got : { *(.got) }
          . = .;
          __u_boot_cmd_start = .;
          .u_boot_cmd : { *(.u_boot_cmd) }
          __u_boot_cmd_end = .;
          . = ALIGN(4);
          __bss_start = .;//為搬運(yùn)代碼提供的符號(hào),來標(biāo)明bss段地址,方便relocate
          .bss : { *(.bss) }
          _end = .; //定義整個(gè)image的結(jié)束地址
          }
          u-boot.lds 是鏈接腳本文件, 我剛開始看這個(gè)鏈接腳本文件時(shí),我疑惑很久,不明白lds中VMA= LMA(資料上很多鏈接腳本包括我們公司項(xiàng)目里面自己寫的lds腳本是通過AT命令設(shè)置過LMA,這樣看起來地址空間分配更清晰),而且整個(gè)image 的VMA按照lds為基址為0x0,而2410芯片不能remap,0x0地址是ROM的區(qū)域,不是運(yùn)行時(shí)RAM的地址,我的理解是代碼段地址應(yīng)該是指向該硬件板內(nèi)存區(qū)域,設(shè)置 .text=TEXT_BASE 而不是lds中的.text=0x0 ,這個(gè)疑點(diǎn)弄的我當(dāng)時(shí)很郁悶,想了很久也沒想沒有搞清楚u-boot這樣鏈接腳本都能讓boot跑起來,當(dāng)我把編譯出來的bin燒到norflash中,uboot居然跑起來了,同時(shí)發(fā)現(xiàn)了一個(gè)問題, u-boot.map 中發(fā)現(xiàn) .text 是從config.mk 定義TEXT_BASE =0x33f80000 ,而不是lds設(shè)置的0x0,這又讓我吃驚,沒清楚是怎么會(huì)事,手上有介紹移植uboot的資料,但都對(duì)uboot鏈接這部分,寫的不夠詳細(xì),知道事config.mk文件搞的鬼,但把makefile文件看了幾遍都沒找不到是怎么回事(還是對(duì)makefile不熟啊!),最后把編譯uboot的過程看了隱藏了個(gè)機(jī)關(guān)是
          arm-linux-ld –Tu-boot-1.1.4boardsmdk2410u-boot.lds –Ttext 0x33f80000
          arm-linux-objcopy --gap-fill =0xff –O binary uboot ubtoot.bin
          不知道uboot設(shè)計(jì)者為什么要在這里加一個(gè)–Ttext 而不是在lds就設(shè)置?而很多移植uboot的資料對(duì)lds文件都有所描述,但這個(gè)重要的細(xì)節(jié)似乎都漏掉了,不知道是不是因?yàn)樘A(chǔ)了,所以沒有講.
          不過最后生成的bin 從上看arm-linux-objcopy --gap-fill =0xff –O binary uboot ubtoot.bin沒有對(duì)鏈接生成的elf文件進(jìn)行重定位,因此它的運(yùn)行地址是config.mk 定義TEXT_BASE為基地址,順序按照lds的順序依次增加的,所以整個(gè)uboot最初運(yùn)行的流程是
          _start reset cpu_init_crit relocate
          這個(gè)部分就是完成初始化,設(shè)SVC32,關(guān)看門狗,關(guān)中斷,設(shè)置時(shí)鐘,初始化SDRAM(為代碼搬運(yùn)到SDRAM做準(zhǔn)備),這些都很簡單
          relocate:
          adr r0, _start
          ldr r1, _TEXT_BASE
          cmp r0, r1
          beq stack_setup
          ldr r2, _armboot_start
          ldr r3, _bss_start
          sub r2, r3, r2
          add r2, r0, r2
          copy_loop:
          ldmia r0!, {r3-r10}
          stmia r1!, {r3-r10}
          cmp r0, r2
          ble copy_loop
          看了下網(wǎng)上的帖子,adr指令,網(wǎng)上很多人被這這個(gè)指令弄郁悶,我看杜春雷的<>P143講,這個(gè)指令是基于PC或者寄存器的,讀到是地址無關(guān)的,一般被編譯器替換為SUB r0, pc,#offset ,不要理解為讀取符合表中_start符號(hào)的地址(0x33f80000).在我們上電開始執(zhí)行時(shí),pc從0開始,所以現(xiàn)在r0值為0 +offset,不等于_TEXT_BASE(0x33f80000).接下來要用到鏈接時(shí)確定的符號(hào)地址了,_armboot_start(0x33f80000)., _bss_start(0x33f97954)這些可以在u-boot.map里面的看到, size of armboot =0x33f97954-0x33f80000 ,把_start:0x0 (norflsh)把.text ,.data的代碼往SDRAM里_TEXT_BASE確定的地址: 0x33f80000搬運(yùn).s3c2410的SDRAM基地址是0x3000_0000,由于uboot支持的這個(gè)board SDRAM是64M,(0x3000_0000---0x3400_0000),所以把u-boot.bin搬運(yùn)到內(nèi)存的高端地址.然后跳到內(nèi)存中執(zhí)行,提高速度.
          之后就relocate stack_setup clear_bss ldr pc, _start_armboot ( ROMRAM)
          _start_armboot: .word start_armboot ( u-boot-1.1.4lib_armboard.c)
          stack_setup , clear_bss設(shè)置堆棧清bss段,都是為進(jìn)入C語言做初始化準(zhǔn)備,通過對(duì)start_armboot鏈接后以及把這個(gè)函數(shù)地址已經(jīng)綁定在RAM中,當(dāng)執(zhí)行完ldr pc, label 指令,程序?qū)臉?biāo)號(hào)綁定地址開始執(zhí)行,從而實(shí)現(xiàn)了從地址無關(guān)程序到地址相關(guān)的轉(zhuǎn)變,我們做代碼搬移也是為了跳轉(zhuǎn)做準(zhǔn)備,如果沒有搬移,直接訪問地址相關(guān),由于RAM中都是隨機(jī)值,一跳轉(zhuǎn)就馬上飛了.當(dāng)進(jìn)入start_armboot C函數(shù),剩下的都沒什么難度了.可以慢分析源碼搞定.2410沒有remap寄存器, relocate時(shí)候要容易些,有remap寄存器的芯片在relocate時(shí)候進(jìn)行remap會(huì)讓情況更復(fù)雜些.不過原理都差不多.
          在進(jìn)入board.c后,uboot還做了一次代碼搬運(yùn)如下,大概如下圖,不過分兩種,一種是把pc機(jī)傳的image通過串口或者網(wǎng)絡(luò)傳到內(nèi)存開始執(zhí)行,或者從nandflash里把應(yīng)用搬到內(nèi)存開始執(zhí)行,不過原理都差不多.
          正好公司內(nèi)部給我們做了板級(jí)初始化培訓(xùn),把硬件板初始流程注意要點(diǎn)整理出來,.和boot這部分初始化對(duì)比,可以發(fā)現(xiàn)硬件板初始化流程都差不多.比較頭痛還是鏈接這部分,這方面的資料感覺太少了,沒人可以指點(diǎn),自己看這部分資料看的很痛苦.
          【CPU核相關(guān)初始化】 【W(wǎng)atchdog初始化】 【GPIO初始化】 【系統(tǒng)時(shí)鐘初始化】 【內(nèi)存初始化】 【模式初始化】 【中斷向量初始化】 【MMU初始化】 【Cache初始化】 【總線初始化】 【語言相關(guān)初始化】 【設(shè)備相關(guān)初始化】
          4.elf 格式和bin格式
          executable and linking format (ELF)重定位,可以參與程序的鏈接(創(chuàng)建一個(gè)程序)和程序的執(zhí)行(運(yùn)行一個(gè)程序) ,主要鏈接,和執(zhí)行,但介紹elf文件的資料很多,沒時(shí)間仔細(xì)看和實(shí)際密切的就是調(diào)試程序時(shí)候都用elf格式調(diào)試,因?yàn)樗苏{(diào)試所需的各種符號(hào), 固化的時(shí)候都是用的bin格式,是可執(zhí)行映象,用objcopy 把elf 轉(zhuǎn)換成bin ,不過網(wǎng)上介紹bin格式的資料很少,只是知道bin程序,只要把pc設(shè)置為bin映象的入口地址,就可以正確執(zhí)行, objcopy 可以對(duì)elf 轉(zhuǎn)換成bin再進(jìn)行地址重定位,不過目前還沒看見過這么干過,對(duì)于elf,和bin這些理解的都不系統(tǒng),資料也很少,工作中,集成開發(fā)工具IDE又把這些設(shè)置都給屏蔽起來,有沒有那個(gè)強(qiáng)人能寫一個(gè)文檔,把這些都系統(tǒng)的講清楚就好了!
          順便問下,論壇上上海的多不多,大家找工作都是在網(wǎng)上找的?有個(gè)MM拉我去上海,雖然對(duì)現(xiàn)在工作很滿意,不過MM比工作更重要,要我做選擇,只有去上海了,不過在51job上投了點(diǎn)簡歷,都石沉大海,按理說2年也不短了,至少也會(huì)冒一個(gè)泡的,有沒有上海的能夠指點(diǎn)下,你們?cè)谏虾J趺凑蚁嚓P(guān)工作的?
          補(bǔ)充一個(gè)當(dāng)時(shí)找資料看見對(duì)網(wǎng)上一個(gè)帖子,感覺寫的很精辟的,關(guān)于地址無關(guān)的解釋,網(wǎng)頁地址被改成相當(dāng)路徑了,就沒辦法地址粘貼出來,現(xiàn)在把原文粘貼出來.
          關(guān)鍵詞: 地址無關(guān)
          術(shù)語
          地址無關(guān): 編譯地址不等于運(yùn)行地址.
          地址相關(guān): 編譯地址等于運(yùn)行地址.
          常見的一些Boot(如, U-Boot, VIVI)和Linux Kernel代碼開始的一段是位置無關(guān)的, 意思就是說運(yùn)行地址與編譯地址無關(guān). 如, Kernel編譯地址是0xc0008000, 而運(yùn)行地址是0x30008000.
          為什么?
          為什么代碼的編譯地址和運(yùn)行地址會(huì)不相等呢? 原因主要有以下幾種: 1) 對(duì)于Boot, 用于存放Boot代碼的存儲(chǔ)器容量小于代碼量. 如, Boot片有4K, 而代碼通常有50-60K. 這樣, 通常會(huì)在前4K代碼里, 讓Boot把自己復(fù)制到RAM, 再接著運(yùn)行.這里我們需要作出一個(gè)選擇, 是讓前面的代碼與地址相關(guān), 還是讓后面的代碼與地址相關(guān)呢? 顯然我們會(huì)選擇前面一段代碼量小的與地址無關(guān). 2) 對(duì)于Linux Kernel, 它是運(yùn)行在虛擬地址空間的, 如0xc0008000, 但在MMU打開之前, 通常這個(gè)地址是
          不存在的, 也就是說在MMU打開之前, Kernel的代碼必須是地址無關(guān)的.
          怎么辦?
          對(duì)于位置無關(guān)的代碼, 尋址是基于pc值的, 在pc值上+/-一個(gè)偏移值, 得到運(yùn)行地址.以ARM為例, 用adr來尋址, adr的實(shí)際上是一個(gè)宏指令, 在代碼編譯時(shí), 會(huì)被編譯器替換成對(duì)pc的+/-運(yùn)算
          這里要注意, 對(duì)pc的+/-運(yùn)行顯然是有一個(gè)地址范圍的, 所以我們?cè)谏厦孢x擇代碼量小的地址無關(guān), 是很明智的.
          而訪問地址相關(guān)的代碼, 只需要使用其它的尋址指令就行了. 但在這之前, 必須保證代碼被放在正確的地址上, 所以通常都會(huì)有一個(gè)復(fù)制代碼的過程, 然后就是跳轉(zhuǎn)到一個(gè)標(biāo)號(hào), 地址相關(guān)代碼就開始運(yùn)行了.


          關(guān)鍵詞: armBOOT閱讀筆

          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁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); })();