加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

smdk2410启动代码分析

发布时间:2020-12-15 18:30:11 所属栏目:百科 来源:网络整理
导读:首先根据链接脚本我们知道程序从 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?????

首先根据链接脚本我们知道程序从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?汇编的时候感觉adrldr要好用,因为它不用管程序是如何链接的,有时候使用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-4bit10011即为svn模式,之后67位置成1表示将禁止irqfiq


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,aliasweak属性的作用是将这个函数声明为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是中断子屏蔽寄存器,CLKDIVNclock divisor register,用来设置FCLKHCLKPCLK三者的比例。


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行,关闭mmucache,这里249行,将1398bit清零(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。


上面的几条指令都是协处理器指令,比较固定,看2410datasheet就可以找到。


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,因此只需要设置BWSCONBANKCONxx0-5),而BANK6,BANK7SDRAM,除了设置BWSCONBANKCONxx67),还需要设置其他四个寄存器,而这13个寄存器的地址是连续的,BWSCON是第一个寄存器,详细情况件2410datasheet


?


137-138行,SMRDATA154-167行这13个寄存器(后面讨论)的值的开始地址,137行将其保存在r0中,_TEXT_BASE的值为TEXT_BASE,而TEXT_BASE = 0x33F80000u-boot-2009.03/board/samsung/smdk2410/config.mk中进行定义,138r10x33F80000


139行,计算出SMRDATATEXT_BASE之间的偏移保存到r0中,也就是在SRAMSMRDATA数据的首地址,因为u-boot.bin之前是经过链接的,因此链接时在程序中SMRDATA数据的首地址是相对TEXT_BASE的,而现在数据在SRAM中的时候,SMRDATA数据的首地址是相对0的,因此当前SRAM中的SMRDATA数据的首地址?= SMRDATATEXT_BASE之间的偏移-0 = SMRDATATEXT_BASE之间的偏移。


140行将?BWSCON的寄存器的地址保存在r1中,141r2r0+13*4,其实就是最后一个寄存器的值的地址,即167行对应的数据的地址,它是用来控制循环的。


142-146行,循环的将SMRDATA13个数据写入存储控制器的13个寄存器中。


149行,返回调用的函数?cpu_init_crit,这样我们又回到了start.S中,继续执行到start.S266-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_initcopy数据的方法,这里就很好明白了,187行,将_armboot_start对应的地址传到r2里,188行将_bss_start对应的对应的地址传给r3,_armboot_startstart.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_BASECONFIG_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语言代码就不说了,很容易看懂的。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读