U-Boot,全称Universal Boot Loader,是遵循GPL条款的开放源码项目,是从FADSROM、8xxROM、PPCBOOT逐步发展演化而来的。其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是相应的Linux内核源程序的简化,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点。但是U-Boot不仅仅支持嵌入式Linux系统的引导,当前,它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS嵌入式操作系统。其目前要支持的目标操作系统是OpenBSD, NetBSD, FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX, RTEMS, ARTOS。这是U-Boot中Universal的一层含义,另外一层含义则是U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、 x86、ARM、NIOS、XScale等诸多常用系列的处理器。这两个特点正是U-Boot项目的开发目标,即支持尽可能多的嵌入式处理器和嵌入式操作系统。
就目前来看,U-Boot对PowerPC系列处理器支持最为丰富,对Linux的支持最完善。其它系列的处理器和操作系统基本是在2002年11 月PPCBOOT改名为U-Boot后逐步扩充的。从PPCBOOT向U-Boot的顺利过渡,很大程度上归功于U-Boot的维护人德国DENX软件工程中心Wolfgang Denk[以下简称W.D]本人精湛专业水平和持着不懈的努力。当前,U-Boot项目正在他的领军之下,众多有志于开放源码BOOT LOADER移植工作的嵌入式开发人员正如火如荼地将各个不同系列嵌入式处理器的移植工作不断展开和深入,以支持更多的嵌入式操作系统的装载与引导。
选择U-Boot的理由如下:
1、开放源码;
2、支持多种嵌入式操作系统内核,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS;
3、支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale;
4、较高的可靠性和稳定性;
5、较高的可靠性和稳定性;
6、高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布等;
7、丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等;
8、较为丰富的开发调试文档与强大的网络技术支持;
本文将从整体架构出发,尽自己的理解分析u-boot源码及其启动原理,之后再介绍下移植u-boot时的一些技巧,最后将以叙述移植的实例的方式来阐述其使用方法。
二、程序架构
要使用u-boot,首先需要搞清楚它的程序架构,要实现启动开发板需要修改哪些文件,下面列举了uboot的主要目录结构:
- board:目标板相关文件,主要包含SDRAM、Flash的驱动;
- common:独立于处理器体系结构的通用代码,如内存大小探测与故障检测等,它实现了u-boot的所有命令,其中内置了一个shell脚本解释器(hush.c,a prototype bourne shell grammar parser),busybox中也使用了它;
- cpu:与处理器相关的文件,如mpc8xx子目录下有串口、网口、LCD驱动及中断初始化等文件。其中cpu.c负责初始化CPU、设置指令cache和数据cache等,interrupt.c负责设置系统的各种中断和异常,如快速中断、开关中断、时钟中断、软件中断、预取中止和未定义指令等,start.S负责u-boot启动时执行的第一个文件,它主要设置系统堆栈和工作方式,为跳转到C程序入口点做准备;
- driver:通用设备驱动,如CFI Flash驱动(目前对INTEL Flash支持较好)
- doc:U-Boot的说明文档;
- examples:可在U-Boot下运行的示例程序,如hello_world.c、timer.c;
- include:U-Boot头文件,注意:configs子目录下与目标板相关的配置头文件是移植过程中经常要修改的文件;
- lib_xxx:处理器体系相关的文件,如lib_ppc, lib_arm目录分别包含与PowerPC、ARM体系结构相关的文件,lib_generic为通用的库函数实现;
- net:与网络功能相关的文件目录,如bootp、nfs、sntp、tftp;
- post:上电自检文件目录,目前仍有待于进一步完善;
- rtc:RTC驱动程序;
- tools:用于创建U-Boot S-RECORD和BIN镜像文件的工具;
-fs:文件系统程序,包括ext2、Jffs2等;
-disk:硬盘接口程序。
在board目录下的每个子平台目录内,都有一个连接脚本文件u-boot.lds,从中可以找到u-boot的函数入口。另外,该目录下还有一个config.mk文件,用于设置TEXT_BASE的地址,该地址就是希望运行的地址、链接地址。
u-boot 是一个层次式结构,要让它跑起来,应当至少提供串口驱动(UART Driver)、以太网驱动(Ethernet Driver)、Flash 驱动(Flash 驱动)以及USB 驱动(USB Driver)。目前,通过USB 口下载程序显得不是十分必要,所以暂时没有移植USB 驱动。驱动层之上是u-boot 的应用,command 通过串口提供人机界面。
三、代码分析
本文的代码分析主要针对freescale的PowerPC芯片mpc83系列,从u-boot启动的过程来分析其源代码,目前大多数的bootloader都分为了Stage1和Stage2两个部分启动,依赖于CPU体系结构的代码常放在Stage1且常用汇编语言实现,在u-boot中功能代码集中在cpu/mpc83xx/start.S中,它包括从系统上电后在基地址开始执行的部分,它运行在flash中,包括对cpu寄存器的初始化和将Stage2的代码拷贝到SDRAM中的代码。而Stage2则用于实现复杂的应用,用C也有更好的可读性和移植性,主要功能代码集中在lib_ppc/board.c中,通过指定一系列的初始化函数表,实现对系统的初始化工作。一般情况下,u-boot编译后的程序不超过100k,且Stage1的代码编译后的大小一般不超过10k,。
以下内容属转载,虽是ARM,但PowerPC与之类似:
*************************************************************************
*/
注:ARM微处理器支持字节(8位)、半字(16位)、字(32位)3种数据类型
@向量跳转表,每条占四个字节(一个字),地址范围为0x0000 0000~@0x0000 0020
@ARM体系结构规定在上电复位后的起始位置,必须有8条连续的跳
@转指令,通过硬件实现。他们就是异常向量表。ARM在上电复位后,@是从0x00000000开始启动的,其实如果bootloader存在,在执行
@下面第一条指令后,就无条件跳转到start_code,下面一部分并没@执行。设置异常向量表的作用是识别bootloader。以后系统每当有@异常出现,则CPU会根据异常号,从内存的0x00000000处开始查表@做相应的处理
/******************************************************
;当一个异常出现以后,ARM会自动执行以下几个步骤:
;1.把下一条指令的地址放到连接寄存器LR(通常是R14).---保存位置
;2.将相应的CPSR(当前程序状态寄存器)复制到SPSR(备份的程序状态寄存器)中---保存CPSR
;3.根据异常类型,强制设置CPSR的运行模式位
;4.强制PC(程序计数器)从相关异常向量地址取出下一条指令执行,从而跳转到相应的异常处理程序中
*****************************************************************************/
首先来看下Stage1的过程,系统主要实现了一下的功能:
1、指定入口函数
一个可执行的镜像必须要有且只有一个全局入口,通常情况下,这个入口函数是放在ROM的起始位置,而它是由处理器中断复位向量来决定的,代码如下:
- <pre name="code" class="cpp">. = EXC_OFF_SYS_RESET
- .globl _start
- _start: /* time t 0 */
- li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH*/
- nop
- b boot_cold
- . = EXC_OFF_SYS_RESET + 0x10
- .globl _start_warm
- _start_warm:
- li r21, BOOTFLAG_WARM /* Software reboot */
- b boot_warm
- </pre><br>
- <pre></pre>
- <p></p>
- <pre></pre>
- <p></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun"><span style="font-size:18px"><span style="color:rgb(255,0,0)">需要注意的是,我们必须自己告诉编译器这个入口,而这个工作就是修改链接脚本文件(lds)。</span>由上可见,函数执行开始后,一个立即读取指令后就是一个跳转语句。一般情况下(上电、复位等),程序都会执行boot_cold,通过调用系统复位中断从System reset偏移向量0x100来获取指令,每个中断向量有256个字节的空间。另外,与start.s文件在一起的也有一个config.mk文件,该文件用于定义编译选项。通过链接地址TEXT_BASE和运行地址.start的不同决定是否要复制代码。</span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px">2、设置异常向量(Exception Vector)</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 异常向量表也可称为中断向量表,在mpc83xx中,它是以0x100的偏移量连续分布的,基地址的值取决于MSR[IP],当它为0是,基地址为0x00000000,为1时,基地址为0xfff00000。该值是由启动方式决定的。源码如下:</span></p>
- <pre name="code" class="cpp">/*
- * Vector Table
- */
- .globl _start_of_vectors
- _start_of_vectors:
- /* Machine check */
- STD_EXCEPTION(0x200, MachineCheck, MachineCheckException)
- /* Data Storage exception. */
- STD_EXCEPTION(0x300, DataStorage, UnknownException)
- /* Instruction Storage exception. */
- STD_EXCEPTION(0x400, InstStorage, UnknownException)
- /* External Interrupt exception. */
- #ifndef FIXME
- STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt)
- #endif
- /* Alignment exception. */
- . = 0x600
- Alignment:
- EXCEPTION_PROLOG(SRR0, SRR1)
- mfspr r4,DAR
- stw r4,_DAR(r21)
- mfspr r5,DSISR
- stw r5,_DSISR(r21)
- addi r3,r1,STACK_FRAME_OVERHEAD
- li r20,MSR_KERNEL
- rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */
- rlwimi r20,r23,0,25,25 /* copy IP bit from saved MSR */
- lwz r6,GOT(transfer_to_handler)
- mtlr r6
- blrl
- .L_Alignment:
- .long AlignmentException - _start + EXC_OFF_SYS_RESET
- .long int_return - _start + EXC_OFF_SYS_RESET
- ….</pre><br>
- <span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left">这里的代码太长了,就没再粘贴,有兴趣的可以下载源码看一下,上面的只包括了机器校验、数据存储异常、指令存储异常等异常处理函数,由上也可以看到其连续存储的特性。</span><br>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px">3、初始化CPU相关</span><span style="font-family:Arial; font-size:14px; line-height:26px; text-align:left"></span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 下面为初始化CPU的代码,实现的功能依次为屏蔽watchdog、初始化中断控制寄存器、清空Cache、关闭MMU等。</span></p>
- <pre name="code" class="csharp"> .globl init_e300_core
- init_e300_core: /* time t 10 */
- /* Initialize machine status; enable machine check interrupt */
- li r3, MSR_KERNEL /*设置MSR,允许数据\指令复制以及Machine check*/
- rlwimi r3, r5, 0, 25, 25 /* preserve IP bit set by HRCW */
- #ifdef DEBUG
- rlwimi r3, r5, 0, 21, 22 /* debugger might set SE & BE bits */
- #endif
- SYNC /* Some chip revs need this... */
- mtmsr r3
- SYNC
- mtspr SRR1, r3 /* Make SRR1 match MSR 中断相关*/
- lis r3, CFG_IMMR@h
- #if defined(CONFIG_WATCHDOG)
- /* Initialise the Wathcdog values and reset it (if req) */
- lis r4, CFG_WATCHDOG_VALUE
- ori r4, r4, (SWCRR_SWEN | SWCRR_SWRI | SWCRR_SWPR)
- stw r4, SWCRR(r3)
- /* and reset it */
- li r4, 0x556C
- sth r4, SWSRR@l(r3)
- li r4, -0x55C7
- sth r4, SWSRR@l(r3)
- #else
- /* 关闭Wathcdog */
- lwz r4, SWCRR(r3)
- /* Check to see if its enabled for disabling
- once disabled by SW you can't re-enable */
- andi. r4, r4, 0x4
- beq 1f
- xor r4, r4, r4
- stw r4, SWCRR(r3)
- 1:
- #endif /* CONFIG_WATCHDOG */
- /* Initialize the Hardware Implementation-dependent Registers */
- /* HID0 also contains cache control */
- /*------------------------------------------------------*/
- lis r3, CFG_HID0_INIT@h
- ori r3, r3, CFG_HID0_INIT@l
- SYNC
- mtspr HID0, r3
- lis r3, CFG_HID0_FINAL@h
- ori r3, r3, CFG_HID0_FINAL@l
- SYNC
- mtspr HID0, r3
- lis r3, CFG_HID2@h
- ori r3, r3, CFG_HID2@l
- SYNC
- mtspr HID2, r3
- /* 关闭MMU功能,先清空所有BAT (块地址转换)*/
- xor r0, r0, r0
- mtspr DBAT0U, r0
- mtspr DBAT0L, r0
- mtspr DBAT1U, r0
- mtspr DBAT1L, r0
- mtspr DBAT2U, r0
- mtspr DBAT2L, r0
- mtspr DBAT3U, r0
- mtspr DBAT3L, r0
- mtspr IBAT0U, r0
- mtspr IBAT0L, r0
- mtspr IBAT1U, r0
- mtspr IBAT1L, r0
- mtspr IBAT2U, r0
- mtspr IBAT2L, r0
- mtspr IBAT3U, r0
- mtspr IBAT3L, r0
- SYNC
- /* 禁用tlb(快表) */
- li r3, 32
- mtctr r3
- li r3, 0
- 1: tlbie r3
- addi r3, r3, 0x1000
- bdnz 1b
- SYNC
- /* Done! */
- Blr</pre><br>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 这里需要注意的是,当程序在Flash中运行时,执行程序跳转时使用了跳转指令,但是不是使用绝对地址的跳转(即直接对PC操作)。因为若使用绝对地址,那么程序的取址就是相对于当前PC位置向前或向后的一段空间,而不会跳进SDRAM中。</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px">4、初始化内存控制器</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> PowerPC处理器初识化内存控制器就是通过操作BAT以及TLB来实现的,将IBAT0~7以及DBAT0~7初始化,并禁用TLB,代码如下:</span></p>
- <pre name="code" class="cpp">/* setup_bats - set them up to some initial state */
- .globl setup_bats
- setup_bats:
- addis r0, r0, 0x0000
- /* IBAT 0 */
- addis r4, r0, CFG_IBAT0L@h
- ori r4, r4, CFG_IBAT0L@l
- addis r3, r0, CFG_IBAT0U@h
- ori r3, r3, CFG_IBAT0U@l
- mtspr IBAT0L, r4
- mtspr IBAT0U, r3
- isync
- /* DBAT 0 */
- addis r4, r0, CFG_DBAT0L@h
- ori r4, r4, CFG_DBAT0L@l
- addis r3, r0, CFG_DBAT0U@h
- ori r3, r3, CFG_DBAT0U@l
- mtspr DBAT0L, r4
- mtspr DBAT0U, r3
- isync
- #ifdef CONFIG_HIGH_BATS
- /* IBAT 4 */
- addis r4, r0, CFG_IBAT4L@h
- ori r4, r4, CFG_IBAT4L@l
- addis r3, r0, CFG_IBAT4U@h
- ori r3, r3, CFG_IBAT4U@l
- mtspr IBAT4L, r4
- mtspr IBAT4U, r3
- isync
- /* DBAT 4 */
- addis r4, r0, CFG_DBAT4L@h
- ori r4, r4, CFG_DBAT4L@l
- addis r3, r0, CFG_DBAT4U@h
- ori r3, r3, CFG_DBAT4U@l
- mtspr DBAT4L, r4
- mtspr DBAT4U, r3
- isync
- #endif
- /* Invalidate TLBs.
- * -> for (val = 0; val < 0x20000; val+=0x1000)
- * -> tlbie(val);
- */
- lis r3, 0
- lis r5, 2
- 1:
- tlbie r3
- addi r3, r3, 0x1000
- cmp 0, 0, r3, r5
- blt 1b
- blr</pre><br>
- <br>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><br>
- </span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px">上面只贴出了高低位各一个BAT的操作代码,其他的类似。</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px">5、复制程序到RAM</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> PowerPC中此段程序并不返回,在将程序代码全部复制到ROM中后,将会直接继续在RAM中运行:</span></p>
- <pre name="code" class="cpp">/*完成了代码复制,不返回,直接调用in_ram执行*/
- addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET
- mtlr r0
- blr
- .globl relocate_code
- relocate_code:
- mr r1, r3 /* 创建一个新的栈指针 */
- mr r9, r4 /* 备份 */
- mr r10, r5
- mr r3, r5 /* r3:拷贝的终点 */
- lis r4, CFG_MONITOR_BASE@h /* r4:拷贝的起点 */
- ori r4, r4, CFG_MONITOR_BASE@l
- lwz r5, GOT(__init_end)
- sub r5, r5, r4 /* r5:拷贝的长度 */
- li r6, CFG_CACHELINE_SIZE /* Cache Line Size */
- /* New GOT-PTR = (old GOT-PTR - CFG_MONITOR_BASE) + Destination Address */
- sub r15, r10, r4
- /* First our own GOT */
- add r14, r14, r15
- /* then the one used by the C code */
- add r30, r30, r15
- /* Now relocate code */
- cmplw cr1,r3,r4
- addi r0,r5,3
- srwi. r0,r0,2
- beq cr1,4f /* In place copy is not necessary */
- beq 7f /* Protect against 0 count */
- mtctr r0
- bge cr1,2f
- la r8,-4(r4)
- la r7,-4(r3)
- /* copy */
- 1: lwzu r0,4(r8)
- stwu r0,4(r7)
- bdnz 1b
- addi r0,r5,3
- srwi. r0,r0,2
- mtctr r0
- la r8,-4(r4)
- la r7,-4(r3)
- /* and compare */
- 20: lwzu r20,4(r8)
- lwzu r21,4(r7)
- xor. r22, r20, r21
- bne 30f
- bdnz 20b
- b 4f
- /* compare failed */
- 30: li r3, 0
- blr
- 2: slwi r0,r0,2 /* re copy in reverse order ... y do we needed it? */
- add r8,r4,r0
- add r7,r3,r0
- 3: lwzu r0,-4(r8)
- stwu r0,-4(r7)
- bdnz 3b
- /*
- * Now flush the cache: note that we must start from a cache aligned
- * address. Otherwise we might miss one cache line.
- */
- 4: cmpwi r6,0
- add r5,r3,r5
- beq 7f /* Always flush prefetch queue in any case */
- subi r0,r6,1
- andc r3,r3,r0
- mr r4,r3
- 5: dcbst 0,r4
- add r4,r4,r6
- cmplw r4,r5
- blt 5b
- sync /* Wait for all dcbst to complete on bus */
- mr r4,r3
- 6: icbi 0,r4
- add r4,r4,r6
- cmplw r4,r5
- blt 6b
- 7: sync /* Wait for all icbi to complete on bus */
- isync</pre><br>
- <br>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px">6、初始化堆栈</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 对于mpc83xx系列,初始化堆栈代码如下:</span></p>
- <p></p><pre name="code" class="cpp">/* set up the stack pointer in our newly created cache-ram (r1) */
- lis r1, (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET)@h
- ori r1, r1, (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET)@l
- li r0, 0 /* Make room for stack frame header and */
- stwu r0, -4(r1) /* clear final stack frame so that */
- stwu r0, -4(r1) /* stack backtraces terminate cleanly */</pre><br>
- <br>
- <p></p>
- <p><br>
- </p>
- <p></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px">7、跳转到Stage2入口处</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 这也是Stage1的最后一步,程序在执行到这一步后,基本的硬件初始化工作也就完成了,下面是跳转的代码:</span></p>
- <pre name="code" class="cpp">GET_GOT /* initialize GOT access */
- /* r3: IMMR */
- lis r3, CFG_IMMR@h
- /* run low-level CPU init code (in Flash)*/
- bl cpu_init_f
- /* r3: BOOTFLAG */
- mr r3, r21
- /* run 1st part of board init code (in Flash)*/
- bl board_init_f</pre><br>
- <br>
- <p><br>
- </p>
- <p></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px">其中board_init_f就是Stage2的函数入口。</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 由上可以看出start.S的流程为:异常向量——上电复位后进入复位异常向量——跳到启动代码处——设置处理器进入管理模式——关闭看门狗——关闭中断——设置时钟分频——关闭MMU和CACHE——进入lowlever_init.S——检查当前代码所处的位置,如果在FLASH中就将代码搬移到RAM中。至此,Stage1分析到此结束。</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 跳转到board_init_f后,程序开始执行Stage2阶段,代码多为C。</span></p>
- <br>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px">这里首先更正下上一篇中的一个错误,最后一步中的跳转代码当时一时仓促贴错了,先改正如下:</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px">7、跳转到Stage2入口处</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 这也是Stage1的最后一步,程序在执行到这一步后,基本的硬件初始化工作也就完成了,下面是跳转的代码:</span></p>
- <pre name="code" class="cpp">clear_bss:
- /* 执行清空bss操作 */
- lwz r3,GOT(__bss_start)
- #if defined(CONFIG_HYMOD)
- /*
- * For HYMOD - the environment is the very last item in flash.
- * The real .bss stops just before environment starts, so only
- * clear up to that point.
- * taken from mods for FADS board
- * 检查当前代码位置
- */
- lwz r4,GOT(environment)
- #else
- lwz r4,GOT(_end)
- #endif
- /* 计算跳转 */
- cmplw 0, r3, r4
- beq 6f
- li r0, 0
- 5:
- stw r0, 0(r3)
- addi r3, r3, 4
- cmplw 0, r3, r4
- bne 5b
- 6:
- mr r3, r9 /* Global Data pointer */
- mr r4, r10 /* Destination Address */
- bl board_init_r</pre><br>
- <br>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px">其中board_init_r就是Stage2的函数入口。</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 由上可以看出start.S的流程为:异常向量——上电复位后进入复位异常向量——跳到启动代码处——设置处理器进入管理模式——关闭看门狗——关闭中断——设置时钟分频——关闭MMU和CACHE——进入low level初始化代码——检查当前代码所处的位置,如果在FLASH中就将代码搬移到RAM中。至此,Stage1分析到此结束。</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 跳转到board_init_r后,程序开始执行Stage2阶段,代码多为C。</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px">望谅解。</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 在上篇分析完Stage1的汇编代码后,又细看了两个C函数,也是属于很重要的初始化函数,所以,在分析Stage2的代码前,先还要看下Stage1的两个C代码程序,分别为cpu_init_f和board_init_f,二者在汇编程序in_flash中被调用,虽是C代码,但实现的仍是基本的初始化功能,且运行在ROM中,属于Stage1的范畴。</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun"><span style="font-size:18px"><strong> 先看cpu_init_f函数</strong>,它是用来初始化low-level CPU的,主要功能包括建立内存映射map、初始化一些寄存器和UPM(User-Programmable Machine)。</span></span></p>
- <span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"> 首先初始化一个结构体:</span><br>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"></span></span></span></p><pre name="code" class="cpp">gd = (gd_t *) (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET);
- /* Clear initial global data */
- memset ((void *) gd, 0, sizeof (gd_t));</pre><br>
- <br>
- <p></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left">此处的结构体gd_t是一个放在启动后很早就可用的内存中的,就像mpc8xx、mpc82xx的DPRAM,或者是数据cache的locked
- parts,主要用于存放一小部分系统初始化时要用的全局变量,直到初始化内存控制器可用RAM之前,这是我们唯一能用的全局变量。这个区间是很小的,在本区间中只有256个字节。之后就开始配置各种设备的时钟模式,下面是对PCI和DMA的配置:</span><br>
- </span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"></span></span></span></span></p><pre name="code" class="cpp">#ifdef CFG_SCCR_PCICM
- /* PCI & DMA clock mode */
- im->clk.sccr = (im->clk.sccr & ~SCCR_PCICM) |
- (CFG_SCCR_PCICM << SCCR_PCICM_SHIFT);
- #endif</pre><br>
- <p></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px">这个配置的选项是要根据datasheet里的SCCR寄存器来定的,下面是mpc83xx的一个系统时钟控制寄存器的各位:</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <img src="http://hi.csdn.net/attachment/201108/21/0_1313906370IG79.gif" alt="" style="border-top-style:none; border-right-style:none; border-bottom-style:none; border-left-style:none; border-width:initial; border-color:initial"><br>
- </p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 根据说明来对应uboot中include/Mpc83xx.h中的相关配置,不过大多数的CPU型号都已经配置好了,一般无需改动。</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 接下来初始化一些寄存器,如复位控制寄存器,DDR控制驱动寄存器等:</span></p>
- <pre name="code" class="cpp">/* RSR - Reset Status Register - clear all status */
- gd->reset_status = im->reset.rsr;
- im->reset.rsr = ~(RSR_RES);
- /* RMR - Reset Mode Register contains checkstop reset enable*/
- im->reset.rmr = (RMR_CSRE & (1<<RMR_CSRE_SHIFT));
- /* LCRR - Clock Ratio Register */
- im->lbus.lcrr = CFG_LCRR;
- /* Enable Time Base & Decrimenter ( so we will have udelay() )*/
- im->sysconf.spcr |= SPCR_TBEN;
- /* System General Purpose Register */
- im->sysconf.sicrh = CFG_SICRH;</pre><br>
- <br>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"></span></span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 这些都需要对照着Datasheet里的第四章:Reset,Clockig,and Initialization一一比对着看,这样才能加深印象(尽管大多数实际应用中都不用修改)。</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 最后是初始化内存映射,下面代码作用为将bank0映射到Flash的bank0的初始地址,后面还有部分代码,将根据需要映射bank1~7的:</span></p>
- <pre name="code" class="cpp">im->lbus.bank[0].br = CFG_BR0_PRELIM;
- im->lbus.bank[0].or = CFG_OR0_PRELIM;
- im->sysconf.lblaw[0].bar = CFG_LBLAWBAR0_PRELIM;
- im->sysconf.lblaw[0].ar = CFG_LBLAWAR0_PRELIM;</pre><br>
- <span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"> 再来看下board_init_f函数</span><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left">,它主要用于在启动时尽快的提供一个控制台接口(串口)用于输出错误信息,并且初始化内存以便于复制代码。这段代码的编写需要注意:全局变量是只读的,BSS还未初始化,堆栈空间也很小(尽量不要有复杂操作)。最开始先进行一系列的初始化操作,和ARM系列类似,将初始化函数列表放在结构体init_sequence中:</span><br>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"></span></span></span></span></span></p><pre name="code" class="cpp">for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
- if ((*init_fnc_ptr) () != 0) {
- hang ();
- }
- }</pre><br>
- <br>
- <p></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><br>
- </span></span></span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"> 主要包括板件前期初始化、获取CPU及总线时钟、初始化SDRAM时钟、初始化串口等操作。此时内存已经映射好了,就可以在DRAM中运行程序了,接下来我们需要在RAM的末端保存一些数据,包括uboot和Linux不能操作的区域、内核日志缓存、PRAM(被保护的RAM)、LCD帧缓存、监听代码、板件信息等。</span><br>
- </span></span></span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"></span></span></span></span></span></span></p><pre name="code" class="cpp"> /*通过修改gd->ram_size可以使uboot无法访问指定区间*/
- gd->ram_size -= CFG_MEM_TOP_HIDE;
- addr = CFG_SDRAM_BASE + get_effective_memsize();
- #ifdef CONFIG_LOGBUFFER
- #ifndef CONFIG_ALT_LB_ADDR
- /*保存内核日志*/
- addr -= (LOGBUFF_RESERVE);
- debug ("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN, addr);
- #endif
- #endif
- #ifdef CONFIG_PRAM
- /* reserve protected RAM */
- i = getenv_r ("pram", (char *)tmp, sizeof (tmp));
- reg = (i > 0) ? simple_strtoul ((const char *)tmp, NULL, 10) : CONFIG_PRAM;
- addr -= (reg << 10); /* size is in kB */
- debug ("Reserving %ldk for protected RAM at %08lx\n", reg, addr);
- #endif /* CONFIG_PRAM */
- #ifdef CONFIG_LCD
- /* reserve memory for LCD display (always full pages) */
- addr = lcd_setmem (addr);
- gd->fb_base = addr;
- #endif /* CONFIG_LCD */
- #if defined(CONFIG_VIDEO) && defined(CONFIG_8xx)
- /* reserve memory for video display (always full pages) */
- addr = video_setmem (addr);
- gd->fb_base = addr;
- #endif /* CONFIG_VIDEO */
- #ifdef CONFIG_AMIGAONEG3SE
- gd->relocaddr = addr;
- #endif
- /* reserve memory for malloc() arena */
- addr_sp = addr - TOTAL_MALLOC_LEN;
- debug ("Reserving %dk for malloc() at: %08lx\n",
- TOTAL_MALLOC_LEN >> 10, addr_sp);
- /* (permanently) allocate a Board Info struct and a permanent copy of the "global" dat*/
- addr_sp -= sizeof (bd_t);
- bd = (bd_t *) addr_sp;
- gd->bd = bd;
- debug ("Reserving %zu Bytes for Board Info at: %08lx\n",sizeof (bd_t), addr_sp);
- addr_sp -= sizeof (gd_t);
- id = (gd_t *) addr_sp;
- debug ("Reserving %zu Bytes for Global Data at: %08lx\n",
- sizeof (gd_t), addr_sp);</pre><br>
- <br>
- <p></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><br>
- </span></span></span></span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><br>
- </span></span></span></span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"></span></span></span></span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px">接下来的代码比较容易理解,就是将板件信息存储到结构体board_info里,包括串口信息、PHY芯片信息、启动参数、RAM参数、flash信息等。</span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"> 分析完这两个C函数后,Stage1的分析就全部结束了。接下来跳入board_init_r函数中开始Stage2。</span></p>
- <br>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><br>
- </span></span></span></span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><br>
- </span></span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><br>
- </span></span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><br>
- </span></span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><br>
- </span></span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><br>
- </span></span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><span style="line-height:26px; text-align:left; font-family:SimSun; font-size:18px"><br>
- </span></span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><span style="font-family:SimSun; font-size:18px; line-height:26px; text-align:left"><br>
- </span></span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><br>
- </span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><br>
- </span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><br>
- </span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><br>
- </span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><br>
- </span></p>
- <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; line-height:26px; text-align:left">
- <span style="font-family:SimSun; font-size:18px"><br>
- </span></p>