Chinaunix首页 | 论坛 | 博客
  • 博客访问: 819865
  • 博文数量: 117
  • 博客积分: 2583
  • 博客等级: 少校
  • 技术积分: 1953
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-06 22:58
个人简介

Coder

文章分类
文章存档

2013年(1)

2012年(10)

2011年(12)

2010年(77)

2009年(13)

2008年(4)

分类: 嵌入式

2010-02-03 16:57:40


首先根据链接脚本我们知道程序从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,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-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, 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行,关闭mmu和cache,这里249行,将13,9,8bit清零(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。
上面的几条指令都是协处理器指令,比较固定,看2410的datasheet就可以找到。
ip是内部调用暂时寄存器
,即r12寄存器,而非指令指针Instruction Pointer。
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
156     .word ((B0_Tacs
157     .word ((B1_Tacs
158     .word ((B2_Tacs
159     .word ((B3_Tacs
160     .word ((B4_Tacs
161     .word ((B5_Tacs
162     .word ((B6_MT
163     .word ((B7_MT
164     .word ((REFEN
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    */
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             */
190         add     r2, r0, r2              /* r2          */
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中执行代码,进行调试的话,就可以用到了它,但是一般在最终的产品发布的时候一般并不需要这样的。

ADR是取符号相对于PC的值,然后将该值加上PC值从而获得符号的地址。

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, 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_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语言代码就不说了,很容易看懂的。
阅读(1175) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~