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>