天天看點

安卓bootloader:三分鐘讓你徹底了解uboot的啟動與功能

1.  Bootloader簡介

      系統上電後,需要一段程式來進行初始化:關閉看門狗,改變系統時鐘,初始化存儲控制器,将更多的代碼複制到記憶體中等。它就是bootloader。

      bootloader的實作非常依賴具體硬體,在嵌入式系統中,硬體配置千差萬别,即使是相同的CPU,它的外設(比如flash)也可能不同,是以不可能有一個bootloader支援所有的CPU,所有的電路闆。即使是支援CPU架構比較多的UBoot,也不是一拿來就可以使用(除非裡面的配置剛好和你的闆子相同)。需要進行一些配置。

      CPU上電後,會從某個位址開始執行,比如MIPS結構的CPU會從0xBFC00000取第一條指令,而ARM結構的CPU則會從0x00000000開始,嵌入式開發闆中,需要把存儲器件的ROM或Flash等映射到這個位址,Bootloader就存放在這個位址的開始處,一上電就開始執行。(手機中的RAM和ROM分别對應電腦的記憶體和硬碟)

2.  啟動流程。

u-boot系統啟動流程 大多數bootloader都分為stage1和stage2兩部分,u-boot也不例外。

依賴于CPU體系結構的代碼(如裝置初始化代碼等)通常都放在stage1且可以用彙編語言來實作,而stage2則通常用C語言來實作,這樣可以實作複雜的功能,而且有更好的可讀性和移植性。

1.Stage1 start.S代碼結構 u-boot的stage1代碼通常放在start.S檔案中,他用彙編語言寫成,其主要代碼部分如下

(1) 定義入口。: 該工作通過修改連接配接器腳本來完成。

(2)設定異常向量(Exception Vector)。 

(3)設定CPU的速度、時鐘頻率及終端控制寄存器。 

(4)初始化記憶體控制器。 

(5)将ROM中的程式複制到RAM中。 

(6) 關中斷,關看門狗

(7)初始化堆棧,清bss段,為第二階段準備。

(8)轉到RAM中執行,該工作可使用指令ldr pc來完成。

2、Stage2

      C語言代碼部分 lib_arm/board.c中的start arm boot是C語言開始的函數也是整個啟動代碼中C語言的主函數,同時還是整個u-boot(armboot)的主函數,該函數隻要完成如下操作: 

(1)調用一系列的初始化函數。 

(2)初始化儲存設備

(3)初始化簡單硬體如序列槽,lcd等 

(4)初始化相關網絡裝置,填寫IP、MAC位址等。 

(5)進去指令循環(即整個boot的工作循環),接受使用者從序列槽輸入的指令,然後進行相應的工作。

3、U-Boot的啟動順序

主要順序如下圖所示

安卓bootloader:三分鐘讓你徹底了解uboot的啟動與功能

                                 圖為 U-Boot順序

下面就根據代碼進行解釋:

.globl _start                         //u-boot啟動入口

_start: b       reset               //複位向量并且跳轉到reset

ldr pc, _undefined_instruction

ldr pc, _software_interrupt

ldr pc, _prefetch_abort

ldr pc, _data_abort

ldr pc, _not_used

ldr pc, _irq                     //中斷向量

ldr pc, _fiq                     //中斷向量

b  sleep_setting             //跳轉到sleep_setting

并通過下段代碼拷貝到記憶體裡

       relocate:                             //把uboot重新定位到RAM

       adr r0, _start                  // r0 是代碼的目前位置 

       ldr r2, _armboot_start               //r2 是armboot的開始位址

       ldr r3, _armboot_end                //r3 是armboot的結束位址

       sub r2, r3, r2                      // r2得到armboot的大小 

       ldr r1, _TEXT_BASE            // r1 得到目标位址  

       add r2, r0, r2                       // r2 得到源結束位址 

       copy_loop:                             //重新定位代碼

ldmia r0!, {r3-r10}                  //從源位址[r0]中複制

stmia r1!, {r3-r10}                  //複制到目标位址[r1]

cmp  r0, r2                        //複制資料塊直到源資料末尾位址[r2]

       ble copy_loop

系統上電或reset後,cpu的PC一般都指向0x0位址,在0x0位址上的指令是

reset:                                 //複位啟動子程式

mrs r0,cpsr                       //将CPSR狀态寄存器讀取,儲存到R0中

bic r0,r0,#0x1f

orr r0,r0,#0xd3

msr cpsr,r0   

                     //将R0寫入狀态寄存器中

ldr      r0, =pWTCON

mov     r1, #0x0

str       r1, [r0]

mov r1, #0xffffffff

ldr r0, =INTMSK

str r1, [r0]

ldr r2, =0x7ff

ldr r0, =INTSUBMSK

str r2, [r0]

ldr r0, =LOCKTIME

ldr     r1, =0xffffff 

str     r1, [r0]

clear_bss:

        ldr       r0, _bss_start           //找到bss的起始位址 

        add      r0, r0, #4              //從bss的第一個字開始 

        ldr       r1, _bss_end           // bss末尾位址 

        mov      r2, #0x00000000       //清零  

clbss_l:str        r2, [r0]                // bss段空間位址清零循環 

        add     r0, r0, #4

        cmp     r0, r1

        bne      clbss_l

/ * cpu初始化關鍵寄存器

* 設定重要寄存器

* 設定記憶體時鐘

* /

cpu_init_crit:

mov r0, #0

mcr p15, 0, r0, c7, c7, 0 

mcr p15, 0, r0, c8, c7, 0 

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

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

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

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

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

mcr p15, 0, r0, c1, c0, 0

mov ip, lr

#ifndef CONFIG_S3C2440A_JTAG_BOOT

bl memsetup        //調用memsetup子程式(在board/smdk2442memsetup.S)

#endif

mov lr, ip

mov pc, lr                        //子程式傳回

memsetup:   

        mov     r1, #MEM_CTL_BASE

        adrl    r2, mem_cfg_val

        add     r3, r1, #52

1:       ldr     r4, [r2], #4

        str     r4, [r1], #4

        cmp     r1, r3

        bne     1b

mov     pc, lr                 //子程式傳回

ldr r0, _armboot_end               //armboot_end重定位

add r0, r0, #CONFIG_STACKSIZE    //向下配置堆棧空間

sub sp, r0, #12                  //為abort-stack預留個3字

ldr pc, _start_armboot           //跳轉到start_armboot函數入口,start_armboot

字儲存函數入口指針

_start_armboot: .word start_armboot    //start_armboot函數在lib_arm/board.c中實作

從此進入第二階段C語言代碼部分

.align  5

undefined_instruction:               //未定義指令

get_bad_stack

bad_save_user_regs

bl  do_undefined_instruction

.align 5

software_interrupt:                   //軟體中斷

get_bad_stack

bad_save_user_regs

bl  do_software_interrupt

.align 5

prefetch_abort:                      //預取異常中止

get_bad_stack

bad_save_user_regs

bl  do_prefetch_abort

.align 5

data_abort:                          //資料異常中止

get_bad_stack

bad_save_user_regs

bl  do_data_abort

.align 5

not_used:                            //未利用

get_bad_stack

bad_save_user_regs

bl  do_not_used

.align 5

irq:                                   //中斷請求

get_irq_stack

irq_save_user_regs

bl  do_irq

irq_restore_user_regs

.align 5

fiq:                                   //快速中斷請求

get_fiq_stack

irq_save_user_regs

bl  do_fiq

irq_restore_user_regs

sleep_setting:                           //休眠設定

@ prepare the SDRAM self-refresh mode

ldr r0, =0x48000024 @ REFRESH Register

ldr r1, [r0]

orr r1, r1,#(1bd = &bd_data;

memset (gd->bd, 0, sizeof (bd_t));

monitor_flash_len = _armboot_end_data - _armboot_start;

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr){

if ((*init_fnc_ptr)() != 0)

  {

  hang ();

}

}

#if 0

size = flash_init ();             //初始化flash

display_flash_config (size);      //顯示flash的大小

#endif

#ifdef CONFIG_VFD

#  ifndef PAGE_SIZE

#  define PAGE_SIZE 4096

#  endif

addr = (_armboot_real_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);

size = vfd_setmem (addr);

gd->fb_base = addr;

addr += size;

addr = (addr + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);

mem_malloc_init (addr);

#else

mem_malloc_init (_armboot_real_end);

#endif    

#if (CONFIG_COMMANDS & CFG_CMD_NAND)

puts ("NAND:");

nand_init();  

#endif

#ifdef CONFIG_HAS_DATAFLASH

AT91F_DataflashInit();

dataflash_print_info();

#endif

env_relocate ();

#ifdef CONFIG_VFD

drv_vfd_init();

#endif

bd_data.bi_ip_addr = getenv_IPaddr ("ipaddr");

{

  int i;

  ulong reg;

  char *s, *e;

  uchar tmp[64];

  i = getenv_r ("ethaddr", tmp, sizeof (tmp));

  s = (i > 0) ? tmp : NULL;

  for (reg = 0; reg bd->bi_enetaddr);

#endif

#ifdef CONFIG_DRIVER_LAN91C96

if (getenv ("ethaddr")) {

  smc_set_mac_addr(gd->bd->bi_enetaddr);

}

#endif 

if ((s = getenv ("loadaddr")) != NULL) {

  load_addr = simple_strtoul (s, NULL, 16);

#if (CONFIG_COMMANDS & CFG_CMD_NET)

if ((s = getenv ("bootfile")) != NULL) {

  copy_filename (BootFile, s, sizeof (BootFile));

}

#endif 

#ifdef BOARD_POST_INIT

board_post_init ();

#endif

for (;;) {

  main_loop (); 

}

UBOOT 學習心得(UBOOT流程分析):

http://www.360doc.com/content/14/0630/16/17775826_391005628.shtml

高通平台BootLoader的流程 

https://blog.csdn.net/makeyourprogress/article/details/73920431

繼續閱讀