亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 1646 | 回復: 0
打印 上一主題 下一主題

UBOOT學習筆記 [復制鏈接]

論壇徽章:
0
跳轉到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2011-02-06 18:48 |只看該作者 |倒序瀏覽
作者:old five zhang
地址:swust
UBoot版本:記不太清楚了

http://blog.chinaunix.net/space.php?uid=14782631

本文檔是自己學習UBoot的總結,只涉及到自己以前迷惑的地方


初探

執(zhí)行一下make,看下make的過程中發(fā)生了什么。

UNDEF_SYM=`arm_v5t_le-objdump -x lib_generic/libgeneric.a board/dahua_davinci/libdahua_davinci.a cpu/arm926ejs/libarm926ejs.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/sk98lin/libsk98lin.a post/libpost.a post/cpu/libcpu.a common/libcommon.a |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;

查找各源文件中的u_boot_cmd段。

arm_v5t_le-ld -Bstatic -T uboot/Trunk/board/dahua_davinci/u-boot.lds -Ttext 0x81080000  $UNDEF_SYM cpu/arm926ejs/start.o --start-group lib_generic/libgeneric.a board/dahua_davinci/libdahua_davinci.a cpu/arm926ejs/libarm926ejs.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/sk98lin/libsk98lin.a post/libpost.a post/cpu/libcpu.a common/libcommon.a --end-group -L /opt/toolchains/davinci/pro/devkit/arm/v5t_le/bin/../lib/gcc/armv5tl-montavista-linuxeabi/3.4.3 -lgcc  -Map u-boot.map -o u-boot

鏈接各源程序為u-boot可執(zhí)行文件,并生成u-boot.map文件。U-boot中包含了一些調試信息,比如說debug_frame ,debug_info等。u-boot.map中記錄了各函數(shù)/變量的地址。

arm_v5t_le-objcopy --gap-fill=0xff -O srec u-boot u-boot.srec

u-boot生成S記錄文件,這個文件一般在MOTOROLA的機子上有用。

objcopy的作用是拷貝一個目標文件的內容到另一個目標文件中。Objcopy使用GNU BFD庫去讀或寫目標文件。Objcopy可以使用不同于源目標文件的格式來寫目的目標文件(也即是說可以將一種格式的目標文件轉換成另一種格式的目標文件)。通過以上命令行選項可以控制Objcopy的具體操作。

通過使用srec作為輸出目標(使用命令行選項-o srec),Objcopy可以產(chǎn)生S記錄格式文件。
通過使用binary作為輸出目標(使用命令行選項-o binary),Objcopy可以產(chǎn)生原始的二進制文件。使用Objcopy產(chǎn)生一個原始的二進制文件,實質上是進行了一回輸入目標文件內容的內存轉儲。所有的符號和重定位信息都將被丟棄。內存轉儲起始于輸入目標文件中那些將要拷貝到輸出目標文件去的部分的最小虛地址處。
使用Objcopy生成S記錄格式文件或者原始的二進制文件的過程中,-S選項和-R選項可能會比較有用。-S選項是用來刪掉包含調試信息的部分,-R選項是用來刪掉包含了二進制文件不需要的內容的那些部分。

arm_v5t_le-objcopy --gap-fill=0xff -O binary u-boot dhboot.bin

uboot可執(zhí)行文件生成RAW二進制文件。對于在PC上運行的程序或者在設備上運行的應用程序,沒有這一步,因為操作系統(tǒng)會解析并根據(jù)可執(zhí)行文件中的信息加載程序執(zhí)行。但對于系統(tǒng)運行的第一個程序u-boot,沒有誰會去解析加載這個程序,所以,需要將其做成RAW的二進制文件,使其能一開始就能在設備上運行。

連接腳本

對于.lds文件,它定義了整個程序編譯之后的連接過程,決定了一個可執(zhí)行程序的各個段的存儲位置。雖然現(xiàn)在我還沒怎么用它,但感覺還是挺重要的,有必要了解一下。

先看一下GNU官方網(wǎng)站上.lds文件形式的完整描述:

SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
  { contents } >region :phdr =fill
...
}

secnamecontents是必須的,其他的都是可選的。下面挑幾個常用的看看:

1、secname:段名

2、contents:決定哪些內容放在本段,可以是整個目標文件,也可以是目標文件中的某段(代碼段、數(shù)據(jù)段等)

3、start:本段連接(運行)的地址,如果沒有使用ATldadr),本段存儲的地址也是start。GNU網(wǎng)站上說start可以用任意一種描述地址的符號來描述。

4、ATldadr):定義本段存儲(加載)的地址。

看一個簡單的例子:(摘自《2410完全開發(fā)》)

/* nand.lds */
SECTIONS { 
firtst 0x00000000 : { head.o init.o } 
second 0x30000000 : AT(4096) { main.o } 
}

以上,head.o放在0x00000000地址開始處,init.o放在head.o后面,他們的運行地址也是0x00000000,即連接和存儲地址相同(沒有AT指定);main.o放在40960x1000,是AT指定的,存儲地址)開始處,但是它的運行地址在0x30000000,運行之前需要從0x1000(加載處)復制到0x30000000(運行處),此過程也就用到了讀取Nand flash。這就是存儲地址和連接(運行)地址的不同,稱為加載時域和運行時域,可以在.lds連接腳本文件中分別指定。 

arm_v5t_le-ld -Bstatic -T uboot/Trunk/board/dahua_davinci/u-boot.lds -Ttext 0x81080000  $UNDEF_SYM cpu/arm926ejs/start.o --start-group lib_generic/libgeneric.a board/dahua_davinci/libdahua_davinci.a cpu/arm926ejs/libarm926ejs.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/sk98lin/libsk98lin.a post/libpost.a post/cpu/libcpu.a common/libcommon.a --end-group -L /opt/toolchains/davinci/pro/devkit/arm/v5t_le/bin/../lib/gcc/armv5tl-montavista-linuxeabi/3.4.3 -lgcc  -Map u-boot.map -o u-boot

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")

OUTPUT_ARCH(arm)

ENTRY(_start)

SECTIONS

{

. = 0x00000000;

. = ALIGN(4);

.text :

{

  cpu/arm926ejs/start.o (.text)  /*.text段表示代碼段,起始位置由-Ttext選項指定 */

  *(.text)

}

. = ALIGN(4);

.rodata : { *(.rodata) }  /*只讀數(shù)據(jù)段保存已經(jīng)初始化的全局只讀數(shù)據(jù),比如只讀字符串*/

. = ALIGN(4);

.data : { *(.data) }  /*數(shù)據(jù)段保存已經(jīng)初始化的全局數(shù)據(jù) */

. = ALIGN(4);

.got : { *(.got) }

. = .;

__u_boot_cmd_start = .;/* 指定u_boot_cmd, uboot把所有的uboot命令放在該段. */

.u_boot_cmd : { *(.u_boot_cmd) }

__u_boot_cmd_end = .;

. = ALIGN(4);

__bss_start = .;

.bss : { *(.bss) }  /*堆棧段,未初始化的全局變量也保存在此*/

_end = .;

}

__u_boot_cmd_end ,__bss_start,_end相當是在這里定義的變量,變量的值是‘.’的地址,變量的地址也是‘.’的地址。這個在程序里可以引用,但__u_boot_cmd_end ,__bss_start_end它們不占用存儲空間。以__bss_start為例,在反匯編的代碼中,有

                0x8109928c                __u_boot_cmd_version

                0x8109925c                __u_boot_cmd_help

                0x810992a4                __u_boot_cmd_end = .

                0x810992a4                . = ALIGN (0x4)

                0x810992a4                __bss_start = .

.bss            0x810992a4    0x197f8

 *(.bss)

 .bss           0x810992a4        0x8 cpu/arm926ejs/libarm926ejs.a(interrupts.o)

 .bss           0x810992ac       0x30 lib_arm/libarm.a(board.o)

                0x810992ac                monitor_flash_len

                0x810992b0                hwid

從這里來看,__u_boot_cmd_end,__bss_start不占用存儲空間,因為他們的值一樣且與cpu/arm926ejs/libarm926ejs.a(interrupts.o)的起始位置的地址一樣。通常,我們只是去讀__u_boot_cmd_end__bss_start的值去確定一個邊界,例如,在start.S中,

_bss_start:

.word __bss_start

這句話的意思是,在_bss_start這個地址的位置放一個值,該值為_bss_start的地址。

查看其匯編代碼:

81080048 <_bss_start>:

81080048:       810992a4        smlatbhi        r9, r4, r2, r9

可見,81080048這個地址中的值就是810992a4。而810992a4正好是__bss_start的值。

再來看另外一個例子,在do_help()中,C代碼為:

int do_help (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])

{

int i;

int rcode = 0;

if (argc == 1) { /*show list of commands */

int cmd_items = &__u_boot_cmd_end -

&__u_boot_cmd_start; /* pointer arith! */

其匯編代碼為:

8108aca4 <do_help>:

8108aca4:       e1a0c00d        mov     ip, sp

8108aca8:       e92ddef0        stmdb   sp!, {r4, r5, r6, r7, r9, sl, fp, ip, lr, pc}

8108acac:       e3520001        cmp     r2, #1  ; 0x1

8108acb0:       e24cb004        sub     fp, ip, #4      ; 0x4

8108acb4:       e1a04002        mov     r4, r2

8108acb8:       e1a0a003        mov     sl, r3

8108acbc:       e3a07000        mov     r7, #0  ; 0x0

8108acc0:       13a06001        movne   r6, #1  ; 0x1

8108acc4:       e24dd008        sub     sp, sp, #8      ; 0x8

8108acc8:       1a000044        bne     8108ade0 <do_help+0x13c>

8108accc:       e59f3184        ldr     r3, [pc, #388]  ; 8108ae58 <.text+0xae58>

8108acd0:       e59f2184        ldr     r2, [pc, #388]  ; 8108ae5c <.text+0xae5c>

8108ae58:       81098e0c        tsthi   r9, ip, lsl #28

8108ae5c:       810992a4        smlatbhi        r9, r4, r2, r9

可見,對&__u_boot_cmd_end的引用被解析成了對810992a4這個地址,810992a4正好是__u_boot_cmd_end的地址。

(可以在內核中測試一下,修改這個變量,如能修改,說明。打印出這個變量的值和地址。。。。)

Start.S

Start.S的作用是實現(xiàn)UBOOT對中斷的處理以及一些必須要做的低級的工作,比如代碼拷貝等。有三種情況需要拷貝:

1. 因為有些CPU的代碼可以在NOR FLASH中執(zhí)行,有些可以在片內的ROM中執(zhí)行,這樣的話,速度有了限制。所以,在START.S中會有代碼的COPY,將代碼拷貝到RAM

2.程序雖然一開始在RAM中運行,但期待的運行地址與程序一開始運行的地址不一樣。比如說程序一開始運行在0x81000000,但這個位置我們需要存放LINUX內核或者我想將其作為棧的起始位置,那我們就需要把代碼轉移到另外一個位置。

3.還有種情況是代碼開始在片內ROM中執(zhí)行,因為片內ROM容量太小,不包含所有的代碼,也需要拷貝,但這里對于我們所用的CPU不考慮這情況。

程序一開始,程序做一些初始化,比如禁止MMUCACHE,然后就跳到函數(shù)lowlevel_init 中去了。對于我們的程序,lowlever_initboard/dahua_davinci/lowlevel_init.S中定義。這個函數(shù)主要做一些PLL,DDR的初始化。

初始化完成后,程序看是否需要拷貝代碼。

adr r0, _start /* r0 <- current position of code   */

ldr r1, _TEXT_BASE /* test if we run from flash or RAM */

cmp     r0, r1                  /* don't reloc during debug         */

beq     stack_setup

adr r0, _start_start的相對位置加載到r0 到,ADR指令的效果與當前PC指針的位置有關,其匯編代碼為:

81080064:       e24f006c        sub     r0, pc, #108    ; 0x6c

如果代碼一開始在0X0運行,那這里的r0就為0,如果在0x8000運行,那么r0就為0x8000。

ldr r1, _TEXT_BASE_TEXT_BASE的值加載到r1中,效果與PC的位置無關,即是說無論開始在0X0運行,還是在81080000運行,這里r1的值都為0x81080000(_TEXT_BASE=0x81080000)

81080000 <_start>:

81080000:       ea000012        b       81080050 <reset>

cmp     r0, r1比較兩者的值,如果相等,說明是在RAM中運行,如果不等,則是在其它地方運行,需要COPY。

拷貝首先計算拷貝的大小和結束的位置,大小size可以用_bss_start -_armboot_start,結束的地址可以用_start+size?截惖倪^程使用ldmstm的循環(huán)完成。

ldr r2, _armboot_start

ldr r3, _bss_start

sub r2, r3, r2 /* r2 <- size of armboot            */

add r2, r0, r2 /* r2 <- source end address         */

拷貝完成后就是棧的設置,將_TEXT_BASE-CFG_MALLOC_LEN-CFG_GBL_DATA_SIZE_TEXT_BASE 這段空間設為棧,使SP指針的值為_TEXT_BASE-CFG_MALLOC_LEN-CFG_GBL_DATA_SIZE 。

然后就是bss的初始化,將__bss_start__bss_end這段空間的內容初始化為0

接著,程序跳到RAM中執(zhí)行,也就是810800c4這個地址:

ldr pc, _start_armboot

_start_armboot:

.word start_armboot

其匯編代碼為:

810800c0:       e51ff004        ldr     pc, [pc, #-4]   ; 810800c4 <_start_armboot>

810800c4 <_start_armboot>:

810800c4:       810810c0        smlabthi        r8, r0, r0, r1





start_armboot

#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")

/*這個聲明告訴編譯器使用寄存器r8來存儲gd_t類型的指針gd,即這個定義聲明了一個指針,并且指明了它的存儲位置。*/

__asm__ __volatile__("": : :"memory");

/*

1__asm__用于指示編譯器在此插入?yún)R編語句 

2__volatile__用于告訴編譯器,嚴禁將此處的匯編語句與其它的語句重組合優(yōu)化。即:原原本本按原來的樣子處理這這里的匯編。 

3) memory強制gcc編譯器假設RAM所有內存單元均被匯編指令修改,這樣cpu中的registerscache中已緩存的內存單元中的數(shù)據(jù)將作廢。cpu將不得不在需要的時候重新讀取內存中的數(shù)據(jù)。這就阻止了cpu又將registers,cache中的數(shù)據(jù)用于去優(yōu)化指令,而避免去訪問內存。 

4"":::表示這是個空指令。

*/

 C語言中嵌入?yún)R編

   格式: _asm_("asm statements":outputs:inputs:registers-modified)

   其中,"asm statements"是匯編語句表達式,outputs,inputs,register-modified都是可選參數(shù),以冒號隔開,且一次以09編號,如outputs的寄存器是0號,inputs寄存器是1號,往后依次類推。outputs是匯編語句執(zhí)行完后輸出到的寄存器,inputs是輸入到某個寄存器。

   1_asm_("pushl %%eax\n\t" "movl $0,%%eax\n\t" "popl %%eax");

   在嵌入?yún)R編中,寄存器前面要加兩個%,因為gcc在編譯是,會先去掉一個%再輸出成匯編格式。

   2{ register char _res;\

         asm("push %%fs\n\t"

         "movw %%ax,%%fs\n\t"

         "movb %%fs:%2,%%al\n\t"

         "pop %%fs"

         :"=a"(_res):"0"(seg),"m"(*(addr)));\

         _res;}

    movb %%fs:%2,%%al\n\t一句中是把以fs為段地址,以后面的第二號寄存器即后面的seg中的值為偏移地址所對應的值裝入al。"=a"(_res):"0"(seg),"m"(*(addr)))一句中,"=a"(_res)表示把a寄存器中的內容給_res,"0"(seg)表示把seg中的內容給0所對應的寄存器,而0即表示使用和前一個寄存器相同的寄存器,這里即使用a寄存器,也就是說把seg中的內容個a寄存器。

   需要解釋以下的是,a,b,c,d分別表示寄存器eaxebx,ecxedx

                  S,D分別表示寄存器esi,edi

                  r表示任意寄存器

                  0(數(shù)字0,不是o。┍硎臼褂蒙弦粋寄存器


您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復

  

北京盛拓優(yōu)訊信息技術有限公司. 版權所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關心和支持過ChinaUnix的朋友們 轉載本站內容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP