smdk2410启动代码分析
首先根据链接脚本我们知道程序从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语言代码就不说了,很容易看懂的。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |