smdk2410 UBoot 汇编阶段分析
?
首先根据链接脚本我们知道程序从u-boot-2009.03/cpu/arm920t/start.S开始,而且入口是_start,因此我们先看start.S,首先是下面的程序: ? ?40 .globl _start ?41 _start: b?????? start_code ?42???????? ldr???? pc,_undefined_instruction ?43???????? ldr???? pc,_software_interrupt ?44???????? ldr???? pc,_prefetch_abort ?45???????? ldr???? pc,_data_abort ?46???????? ldr???? pc,_not_used ?47???????? ldr???? pc,_irq ?48???????? ldr???? pc,_fiq ?49 ?50 _undefined_instruction: .word undefined_instruction ?51 _software_interrupt:??? .word software_interrupt ?52 _prefetch_abort:??????? .word prefetch_abort ?53 _data_abort:??????????? .word data_abort ?54 _not_used:????????????? .word not_used ?55 _irq:?????????????????? .word irq ?56 _fiq:?????????????????? .word fiq ? 42-49行首先42行直接是一个跳转指令,跳到start_code处,43-49行将相应的变量的值装入pc中,其实也是跳转指令,这8行构成了arm架构的异常向量表,共32个字节,是各种异常处理程序的入口,请注意这是硬件决定的,不可更改,当发生了对应的异常的时候,会自动到地址为0x00-0x1c的异常向量表中查找对应的异常处理程序的地址,然后跳转去执行。这里start_code对应的是reset异常,在以前的u-boot版本都是直接用reset的,现在改成了start_code。 ? Address??? ??????????Exception???????????? Mode in Entry 0x00000000???????????? Reset???????????????? Supervisor 0x00000004???????????? Undefined instruction??? Undefined 0x00000008???????????? Software Interrupt?????? Supervisor 0x0000000C???????????? Abort (prefetch)??????? Abort 0x00000010???????????? Abort (data)??????????? Abort 0x00000014???????????? Reserved????????????? Reserved 0x00000018???????????? IRQ????????????????? IRQ 0x0000001C???????????? FIQ????????????????? FIQ 还有一个问题,了解arm指令的朋友可能会对此处使用ldr有疑问,为什么不用adr了,在平常写arm汇编的时候感觉adr比ldr要好用,因为它不用管程序是如何链接的,有时候使用ldr考虑链接的问题会搞得我们焦头烂额的,但是这里可不能这样用,因为adr是不能跨越段的,即不能通过pc计算出其他段的偏移,如果异常处理程序在其他文件或段中就会出错了。 ? 109 start_code: 110???????? /* 111????????? * set the cpu to SVC32 mode 112?????? ???*/ 113???????? mrs???? r0,cpsr 114???????? bic???? r0,r0,#0x1f 115???????? orr???? r0,#0xd3 116???????? msr???? cpsr,r0 117 118???????? bl coloured_LED_init 119???????? bl red_LED_on ? 113行,将cpsr的内容读到r0中; 114行,bic位清零指令,0x1f=00022222,所以将r0的低5位清成0; 115行,让r0或上0xd3,而0xd3=11010011,即将0,1,4,6,7bit置成了1,模式位对应的0-4bit,10011即为svn模式,之后6,7位置成1表示将禁止irq和fiq。 118行,跳转到函数coloured_LED_init() (在u-boot-2009.03 /lib_arm/board.c中),执行完后返回继续执行red_LED_on(),下面的代码就是这两个函数: ? 125 void inline __coloured_LED_init (void) {} 126 void inline coloured_LED_init (void) __attribute__((weak,alias("__coloured_LED_init"))); 127 void inline __red_LED_on (void) {} 128 void inline red_LED_on (void) __attribute__((weak,alias("__red_LED_on"))); ? 这里很有必要提一下126行,128行,可以看到在声明这两个函数的时候后面加了__attribute__((weak,alias("xxx"))),这是gcc的扩展属性,这里有两个属性weak,alias,weak属性的作用是将这个函数声明为weak,在这种情况下使用这个函数时,如果有这个函数的定义的话就使用这个函数,如果没有这个函数的定义的话就是一个空函数。而alias的作用是别名,即括号中的xxx跟这个函数是别名,跟linux中的alias的意义是一样的。因此执行118行实际上就是执行__coloured_LED_init(),而119行执行的是__red_LED_on (),它们都是空的,也就是什么都不做。 ? 接下来: 135 #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) 136???????? /* turn off the watchdog */ 137 138 # if defined(CONFIG_S3C2400) 139 #? define pWTCON??????????????? 0x15300000 140 #? define INTMSK??????????????? 0x14400008????? /* Interupt-Controller base addresses */ 141 #? define CLKDIVN?????? 0x14800014????? /* clock divisor register */ 142 #else 143 #? define pWTCON??????????????? 0x53000000 144 #? define INTMSK??????????????? 0x4A000008????? /* Interupt-Controller base addresses */ 145 #? define INTSUBMSK???? 0x4A00001C 146 #? define CLKDIVN?????? 0x4C000014????? /* clock divisor register */ 147 # endif 148 149???????? ldr???? r0,=pWTCON 150???????? mov???? r1,#0x0 151???????? str???? r1,[r0] 152 153???????? /* 154????????? * mask all IRQs by setting all bits in the INTMR - default 155????????? */ 156???????? mov???? r1,#0xffffffff 157???????? ldr???? r0,=INTMSK 158???????? str???? r1,[r0] 159 # if defined(CONFIG_S3C2410) 160???????? ldr???? r1,=0x3ff 161???????? ldr???? r0,=INTSUBMSK 162???????? str???? r1,[r0] 163 # endif 164 165???????? /* FCLK:HCLK:PCLK = 1:2:4 */ 166???????? /* default FCLK is 120 MHz ! */ 167???????? ldr???? r0,=CLKDIVN 168???????? mov???? r1,#3 169???????? str???? r1,[r0] 170 #endif? /* CONFIG_S3C2400 || CONFIG_S3C2410 */ ? 138-147行定义相关寄存器的地址,pWTCON是看门狗控制寄存器,INTMSK是中断屏蔽寄存器,INTSUBMSK是中断子屏蔽寄存器,CLKDIVN是clock divisor register,用来设置FCLK,HCLK,PCLK三者的比例。 149行,将pWTCON设置为0,即将看门狗关闭。 153-163行,首先156-158行将INTMSK的所有位置为1,即可屏蔽所有中断,159-163行,表示如果是2410的话,会有级联的中断控制器,因此还需要将子中断屏蔽寄存器的所有中断进行屏蔽。 165-170行,将CLKDIVN设置为3,即设置为FCLK:HCLK:PCLK = 1:2:4 ? 176 #ifndef CONFIG_SKIP_LOWLEVEL_INIT 177???????? bl????? cpu_init_crit 178 #endif ? 177行,如果没有定义过CONFIG_SKIP_LOWLEVEL_INIT,就跳到cpu_init_crit处。 ? 236 #ifndef CONFIG_SKIP_LOWLEVEL_INIT 237 cpu_init_crit: 238???????? /* 239????????? * flush v4 I/D caches 240????????? */ 241???????? mov???? r0,#0 242???????? mcr???? p15,c7,0?? /* flush v3/v4 cache */ 243???????? mcr???? p15,c8,0?? /* flush v4 TLB */ 244 245???????? /* 246????????? * disable MMU stuff and caches 247????????? */ 248???????? mrc???? p15,c1,c0,0 249???????? bic???? r0,#0x00002300???? @ clear bits 13,9:8 (--V- --RS) 250???????? bic???? r0,#0x00000087???? @ clear bits 7,2:0 (B--- -CAM) 251???????? orr???? r0,#0x00000002???? @ set bit 2 (A) Align 252???????? orr???? r0,#0x00001000???? @ set bit 12 (I) I-Cache 253???????? mcr???? p15,0 254 255???????? /* 256????????? * before relocating,we have to setup RAM timing 257????????? * because memory timing is board-dependend,you will 258????????? * find a lowlevel_init.S in your board directory. 259????????? */ 260???????? mov???? ip,lr 261 #if???? defined(CONFIG_AT91RM9200EK) 262 263 #else 264???????? bl????? lowlevel_init 265 #endif 266???????? mov???? lr,ip 267???????? mov???? pc,lr 268 #endif /* CONFIG_SKIP_LOWLEVEL_INIT */ ? 241-243行,失效I/D cache,TLB。 248-253行,关闭mmu和cache,这里249行,将13,9,8bit清零(13—异常向量表基地址:0x0, 9—Disable System Protection,8—Disable ROM Protection),250行,将7,2,0bit清零(7—为0的时候表示小端字节序,2-- Data Cache Disabled,1-- Alignment Fault checking disabled,0—为0的话MMU disabled),251行,将bit 1 设置为1表示Fault checking enabled,252行,将bit 12设置为1表示使能 I-Cache。 上面的几条指令都是协处理器指令,比较固定,看2410的datasheet就可以找到。 260行,因为下面要继续调用子程序,所以需要将之前已经保存在lr中的pc值再保存在ip中,之后261行,将pc值保存到lr中,跳转到lowlevel_init,这跟函数在u-boot-2009.03/board/samsung/smdk2410/lowlevel_init.S下: ? 129 _TEXT_BASE: 130???????? .word?? TEXT_BASE 131 132 .globl lowlevel_init 133 lowlevel_init: 134???????? /* memory control configuration */ 135???????? /* make r0 relative the current location so that it */ 136???????? /* reads SMRDATA out of FLASH rather than memory ! */ 137???????? ldr???? r0,=SMRDATA 138???????? ldr???? r1,_TEXT_BASE 139???????? sub???? r0,r1 140???????? ldr?? ??r1,=BWSCON???? /* Bus Width Status Controller */ 141???????? add???? r2,#13*4 142 0: 143???????? ldr???? r3,[r0],#4 144???????? str???? r3,[r1],#4 145???????? cmp???? r2,r0 146???????? bne???? 0b 147 148???????? /* everything is fine now */ 149???????? mov???? pc,lr 150 151???????? .ltorg 152 /* the literal pools origin */ 153 154 SMRDATA: 155???? .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28)) 156???? .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) 157???? .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC)) 158???? .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)) 159???? .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)) 160???? .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)) 161???? .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)) 162???? .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) 163???? .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) 164???? .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT) 165???? .word 0x32 166???? .word 0x30 167???? .word 0x30 ? 整个lowlevel_init.S文件中就只有这个函数,相关的宏定义非常的长,就不全部贴出来了,这里说说这个函数到底做了什么。因为后面我们不管是将内核copy到内存中,还是在u-boot中执行程序,都涉及到内存,flash,外设等的使用,因此在使用前必须要保证他们可用,而存储控制器就是用来控制它们的,所以我们这里就需要先对其进行初始化。这里需要先简单讨论一下存储控制器,2410平台上存储控制器共有13个寄存器,BANK0-BANK5用来接外设,flash,因此只需要设置BWSCON和BANKCONx(x为0-5),而BANK6,BANK7接SDRAM,除了设置BWSCON和BANKCONx(x为6,7),还需要设置其他四个寄存器,而这13个寄存器的地址是连续的,BWSCON是第一个寄存器,详细情况件2410的datasheet。 ? 137-138行,SMRDATA为154-167行这13个寄存器(后面讨论)的值的开始地址,137行将其保存在r0中,_TEXT_BASE的值为TEXT_BASE,而TEXT_BASE = 0x33F80000在u-boot-2009.03/board/samsung/smdk2410/config.mk中进行定义,138行r1为0x33F80000。 139行,计算出SMRDATA跟TEXT_BASE之间的偏移保存到r0中,也就是在SRAM中SMRDATA数据的首地址,因为u-boot.bin之前是经过链接的,因此链接时在程序中SMRDATA数据的首地址是相对TEXT_BASE的,而现在数据在SRAM中的时候,SMRDATA数据的首地址是相对0的,因此当前SRAM中的SMRDATA数据的首地址 = SMRDATA跟TEXT_BASE之间的偏移-0 = SMRDATA跟TEXT_BASE之间的偏移。 140行将 BWSCON的寄存器的地址保存在r1中,141行r2为r0+13*4,其实就是最后一个寄存器的值的地址,即167行对应的数据的地址,它是用来控制循环的。 142-146行,循环的将SMRDATA的13个数据写入存储控制器的13个寄存器中。 149行,返回调用的函数 cpu_init_crit,这样我们又回到了start.S中,继续执行到start.S的266-267行,在返回调用 cpu_init_crit的地方继续执行180行。 ? 180 #ifndef CONFIG_SKIP_RELOCATE_UBOOT 181 relocate:?????????????????????????????? /* relocate U-Boot to RAM?????????? */ 182???????? adr???? r0,_start????????????? /* r0 <- current position of code?? */ 183???????? ldr???? r1,_TEXT_BASE????????? /* test if we run from flash or RAM */ 184???????? cmp???? r0,r1????????????????? /* don't reloc during debug???????? */ 185???????? beq???? stack_setup 186 187???????? ldr???? r2,_armboot_start 188???????? ldr? ???r3,_bss_start 189???????? sub???? r2,r3,r2????????????? /* r2 <- size of armboot??????????? */ 190???????? add???? r2,r2????????????? /* r2 <- source end address???????? */ 191 192 copy_loop: 193???????? ldmia?? r0!,{r3-r10}?????????? /* copy from source address [r0]??? */ 194???????? stmia?? r1!,{r3-r10}?????????? /* copy to?? target address [r1]??? */ 195???????? cmp???? r0,r2????????????????? /* until source end addreee [r2]??? */ 196???????? ble???? copy_loop 197 #endif? /* CONFIG_SKIP_RELOCATE_UBOOT */ ? ? 这段代码的作用是为了bootloader的第二阶段(即monitor)作准备的,将完整的u-boot代码copy到内存中,后面就跳到内存的代码中去执行,你如果想在u-boot中执行代码,进行调试的话,就可以用到了它,但是一般在最终的产品发布的时候一般并不需要这样的。 ? 182-185行,r0是当前代码的起始地址,而r1是_TEXT_BASE,即正常情况下u-boot在内存中的起始地址,184行,比较了r0,r1就可以判断当前是否在内存中,如果相等表示已经在内存中了,就不用复制了直接跳转到stack_setup,如果不再内存中就还需要将u-boot复制到内存的_TEXT_BASE的地方。 187-197行,看过上面lowerlevel_init中copy数据的方法,这里就很好明白了,187行,将_armboot_start对应的地址传到r2里,188行将_bss_start对应的对应的地址传给r3,_armboot_start在start.S的前面定义,如下: ?77 .globl _armboot_start ?78 _armboot_start: ?79???????? .word _start ? 可以看到_armboot_start 就是_start,也就是程序的开始地址,而_bss_start定义在 ?84 .globl _bss_start ?85 _bss_start: ?86???????? .word __bss_start ? 而__bss_start定义在链接脚本u-boot-2009.03/board/samsung/smdk2410/u-boot.lds中 ?28 SECTIONS ?29 { ?30???????? . = 0x00000000; ?31 ?32???????? . = ALIGN(4); ?33???????? .text????? : ?34???????? { ?35?????????? cpu/arm920t/start.o?? (.text) ?36?????????? *(.text) ?37???????? } ?38 ?39???????? . = ALIGN(4); ?40???????? .rodata : { *(.rodata) } ?41 ?42???????? . = ALIGN(4); ?43???????? .data : { *(.data) } ?44 ?45???????? . = ALIGN(4); ?46???????? .got : { *(.got) } ?47 ?48???????? . = .; ?49???????? __u_boot_cmd_start = .; ?50???????? .u_boot_cmd : { *(.u_boot_cmd) } ?51???????? __u_boot_cmd_end = .; ?52 ?53???????? . = ALIGN(4); ?54???????? __bss_start = .; ?55???????? .bss (NOLOAD) : { *(.bss) . = ALIGN(4); } ?56???????? _end = .; ?57 } ? 之后189行,r2=r3-r2=__bss_start - _start=代码段的长度 190行,r2=r0+r2=_start+代码段的长度=当前代码段的结束地址 192-196行, copy u-boot的代码到内存中来 ? 上面程序在不管是否需要将u-boot copy到内存中去,都会执行到下面的 stack_setup来: ? 199???????? /* Set up the stack???????????????????????????????????????????????? */ 200 stack_setup: 201???????? ldr???? r0,_TEXT_BASE????????? /* upper 128 KiB: relocated uboot?? */ 202??????? ?sub? r0,#CONFIG_SYS_MALLOC_LEN? /* malloc area????????? ????????????*/ 203???????? sub???? r0,#CONFIG_SYS_GBL_DATA_SIZE /* bdinfo??????????????????????? */ 204 #ifdef CONFIG_USE_IRQ 205???????? sub???? r0,#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) 206 #endif 207???????? sub???? sp,#12????????? ???/* leave 3 words for abort-stack??? */ 208 209 clear_bss: 210???????? ldr???? r0,_bss_start????????? /* find start of bss segment??????? */ 211???????? ldr???? r1,_bss_end??????????? /* stop here??????????????????????? */ 212???????? mov???? r2,#0x00000000???????? /* clear??????????????????????????? */ 213 214 clbss_l:str???? r2,[r0]??????????????? /* clear loop...??????????????????? */ 215???????? add???? r0,#4 216???????? cmp???? r0,r1 217???????? ble???? clbss_l 218 219???????? ldr???? pc,_start_armboot 220 221 _start_armboot: .word start_armboot ? 这段代码的作用是建立堆栈,和一些必要的段,同上面,这里也是为了后面服务的,如果你不打算进入下载模式的话,不想在u-boot中执行程序,那么这段代码有没有是无所谓的,因为,内核启动之后是会重新对这些内存地址进行设置的。 201行,r0=_TEXT_BASE,202行,r0=r0- CONFIG_SYS_MALLOC_LEN,这样之后,这是在为malloc预留空间,也就是说_TEXT_BASE下CONFIG_SYS_MALLOC_LEN长的范围是malloc使用的区域。 203行,为全局参数预留内存空间 204-206行,如果打算使用IRQ,FIQ模式,就为他们预留内存空间 207行,为abort异常预留12字节的空间,并将当前的地址赋给sp,这样就为内存栈设置好了,之后如果在u-boot中运行程序时需要使用栈的时候就从这里开始。 209-217行,清除bss段。 219行,跳转到_start_armboot,也就是函数 start_armboot,此函数存放在u-boot-2009.03/lib_arm/board.c,这样就到了u-boot的第二阶段了。 ? ??? 进入了第二阶段,程序其实也就是进入了下载模式,这个阶段只是为了在u-boot环境下运行程序而存在的,即假如你只想在在初始化之后就启动内核的话,这个阶段没有也是没有关系的,这样的话,你就需要更改代码,将复制u-boot完整代码之后的代码全部不要,并加上将内核copy至指定地址,并建好tag list段就好了,设置好r0,r1,r2的值,跳转到内核的起始地址就可以将内核启起来了。这里第二阶段的c语言代码就不说了,很容易看懂的。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |