smdk2410 UBoot 汇编阶段分析

来源:互联网 发布:路由器监控软件 编辑:程序博客网 时间:2024/06/11 23:07
 

首先根据链接脚本我们知道程序从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,r0,#0xd3

116         msr     cpsr,r0

117

118         bl coloured_LED_init

119         bl red_LED_on

 

113行,将cpsr的内容读到r0中;

114行,bic位清零指令,0x1f=00011111,所以将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, 0, r0, c7, c7, 0   /* flush v3/v4 cache */

243         mcr     p15, 0, r0, c8, c7, 0   /* flush v4 TLB */

244

245         /*

246          * disable MMU stuff and caches

247          */

248         mrc     p15, 0, r0, c1, c0, 0

249         bic     r0, r0, #0x00002300     @ clear bits 13, 9:8 (--V- --RS)

250         bic     r0, r0, #0x00000087     @ clear bits 7, 2:0 (B--- -CAM)

251         orr     r0, r0, #0x00000002     @ set bit 2 (A) Align

252         orr     r0, r0, #0x00001000     @ set bit 12 (I) I-Cache

253         mcr     p15, 0, r0, c1, c0, 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,1,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, r0, r1

140         ldr     r1, =BWSCON     /* Bus Width Status Controller */

141         add     r2, r0, #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, r0, 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, r0, #CONFIG_SYS_MALLOC_LEN  /* malloc area                      */

203         sub     r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo                        */

204 #ifdef CONFIG_USE_IRQ

205         sub     r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

206 #endif

207         sub     sp, r0, #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, 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语言代码就不说了,很容易看懂的。

原创粉丝点击