MTK6735 pre-loader源代碼分析


參考http://blog.chinaunix.net/uid-28458801-id-3487199.html

 

一.簡介

 MTK的bootloader分為bootROM + pre-loader[l5]  +U-boot

 

因為bootloader的一部分和系統有關,所以MTK為了不同的應用將它分為兩部分的bootloader:

    (1)第1部分bootloader,也就是MTK內部(in-house)的pre-loader,這部分依賴平台,這部分有BootROM來加載到內部的ISRAM中執行。

    (2)第2部分bootloader,也就是u-boot,這部分依賴操作系統,由pre-loader加載到外部DRAM中執行。負責引導linux操作系統和Android框架,但是從Android 4.1(jelly bean)開始,MTK采用little kernel來替代U-boot。

    

 

正常啟動的主要工作如下:

    (1)設備上電后,Boot ROM開始運行。

    (2)Boot ROM初始化軟件堆棧(softwarestack)、通信端口和可引導存儲設備(比如NAND/EMMC)。

    (3)Boot ROM從存儲器中加載pre-loader到內部SRAM(ISRAM)中,因為這時候還沒有初始化外部的DRAM。

    (4)Boot ROM跳轉到pre-loader的入口處並執行。

    (5)Pre-loader初始化DRAM和加載U-Boot到RAM中。

    (6)Pre-loader跳轉到U-Boot中並執行,然后U-Boot做一些初始化,比如顯示的初始化等。

    (7)U-Boot從存儲器中加載引導鏡像(boot image),包括linux內核和ramdisk(最小文件系統)

    (8)U-Boot跳轉到linux內核並執行。

 

 

二. Pre-loader的過程(procedure)和流程(flow)

    

 

三. Pre-loader啟動過程的分析

3.1 resethandler()

這是pre-loader的入口函數,在bootable/bootloader/preloader/platform/mt6735/src/init/init.s中定義,下面類分下此函數的代碼:

 

[plain]  view plain  copy
  在CODE上查看代碼片 派生到我的代碼片
  1. resethandler :  
  2.     MOV r0, #0  
  3.     MOV r1, #0  
  4.     MOV r2, #0  
  5.     MOV r3, #0  
  6.     MOV r4, #0  
  7.     MOV r5, #0  
  8.     MOV r6, #0  
  9.     MOV r7, #0  
  10.     MOV r8, #0  
  11.     MOV r9, #0  
  12.     MOV r10, #0  
  13.     MOV r11, #0  
  14.     MOV r12, #0  
  15.     MOV sp, #0  
  16.     MOV lr, #0  

 

    r0~r12是通用寄存器,可保存數據和地址,r13(sp)、r14(lr)和r15(pc)是ARM處理器為特殊的任務或是專門的功能指定的寄存器。

 

(1)r13通常用作堆棧指針(sp)

        指向當前處理器模式的堆棧的棧頂,RM處理器針對不同的模式,共有 6 個堆棧指針(SP),其中用戶模式和系統模式共用一個SP,每種異常模式都有各自專用的R13寄存器(SP)。它們通常指向各模式所對應的專用堆棧,也就是ARM處理器允許用戶程序有六個不同的堆棧空間。這些堆棧指針分別為R13、R13_svc、R13_abt、R13_und、R13_irq、R13_fiq。

(2)r14鏈接寄存器(lr),保存調用子程序的返回地址。

(3)r15是程序計數器(pc),其內容是處理器要取的下一條指令的地址。

 

這里是把這些寄存器的內容清零。

 

[plain]  view plain  copy
  在CODE上查看代碼片 派生到我的代碼片
  1. /* set the cpu toSVC32 mode */  
  2.     MRS    r0,cpsr  
  3.     BIC    r0,r0,#0x1f  
  4.     ORR    r0,r0,#0xd3  
  5.     MSR    cpsr,r0  
  6.    
  7.     /* disable interrupt */  
  8.     MRS r0, cpsr  
  9.     MOV r1, #INT_BIT  
  10.     ORR r0, r0, r1  
  11. MSR cpsr_cxsf, r0     

 

最后1行表示把r0寄存器的值寫入cpsr寄存器對應的4個控制域

c  控制域屏蔽 psr[7..0]

x  擴展域屏蔽 psr[15..8]

s  狀態域屏蔽psr[23..16]

f  標志域屏蔽psr[31..24]

注意:區域名必須為小寫字母

 

(1)MRS和MSR指令

MRS: Move to Register from Stateregister

MSR: Move to State register fromRegister

http://blog.csdn.net/mr_raptor/article/details/6556172

 

(2)設置CPU為管理模式和屏蔽中斷

Cpsr (Current Program Status Register)是當前程序狀態寄存器


 

圖4

 

    設置cpsr[7:0]=d3,表示pre-loader禁用中斷請求(interrupt request)和快速中斷請求(fast interrupt request);T=0表示ARM狀態。M4~M0=1011表示管理模式。

[plain]  view plain  copy
  在CODE上查看代碼片 派生到我的代碼片
  1. /* enable I+Z bits */  
  2. MRC p15, 0, ip, c1, c0, 0 /*read SCTLR*/  
  3. ORR ip, ip, #0x1800   /* I+Z bits */  
  4. p15, 0, ip, c1, c0, 0  /*write SCTLR*/  

第1行代碼是通過設置系統控制寄存器(system control register)的I和Z位為1來分別使能Instructioncaching(指令高速緩存)與Program flowprediction(程序流預測)

 

(1)MRC和MCR是協處理器命令,這里的C指coprocessor

MRC{<cond>}p15,<opcode_1>, <Rd>, <CRn>, <CRm>{,<opcode_2>}

<cond>:為指令執行的條件碼。當<cond>忽略時指令為無條件執行

p15:指協處理器CP15

<opcode_1>:操作碼1

<Rd>:ARM處理器的寄存器,對於MRC命令來說是目的寄存器。

<CRn>:協處理器寄存器,可為C0,C1,…,C15,CP15的首要寄存器(primary coprocessor)

<CRm>:CP15的次要(輔助/操作)寄存器(secondary/operational coprocessor)

<opcode_2>:操作碼2

http://blog.chinaunix.net/uid-24517893-id-253685.html

 

(2)MRC p15, 0, ip, c1,c0, 0

        這里我們是怎么知道讀取SCTLR寄存器的值,然后放入到ARM處理器寄存器ip(ip是什么寄存器?),我們先來看《DDI0388F_cortex_a9_r2p2_trm.pdf》下面相關部分:

 

圖5

因為CRn=c1,所以我們來看CP15 c1寄存器部分,如下:


 

圖6

這就是當CRn=c1時我們可以CP15的寄存器,有SCTLR、ACTLR、CPACR等等。可知MRC p15, 0, ip, c1, c0, 0就是讀取系統控制寄存器的值(SystemControl Register),如果要知道系統控制寄存器的具體內容就要詳細看其介紹。

 

[cpp]  view plain  copy
  在CODE上查看代碼片 派生到我的代碼片
  1. ORR ip, ip, #0x1800  
  2. MCR p15, 0, ip, c1,c0, 0  

 

由此可見是要設置SCTLR[12:11]=0b11,我們給出這兩位的說明:


 

圖7

到此我們就可以理解上面代碼的意義了。

 

 

三. Main()

    bootable/bootloader/preloader/platform/mt6735/src/core/main.c


 

    這里要特別注意就是不能在調用bldr_pre_process()之前調用串口輸出信息的函數,比如print(),否則無法啟動,而且還無法再次燒錄。上面這些函數都是以bldr開頭,這是bootloader的簡稱。

    main

        mtk_uart_init(UART_SRC_CLK_FRQ, CFG_LOG_BAUDRATE);  //初始化UART, 設置波特率為921600

       bldr_pre_process(); //3.2.1 

 

 

3.1  bldr_pre_process()

    /* essential hardware initialization. e.g. timer, pll, uart... */

    platform_pre_init();

    g_boot_mode = NORMAL_BOOT;

     /* hardware initialization */

    platform_init();


 

 

3.1.1    platform_pre_init()

 

此函數主要是做一些基本的硬件初始化,包括定時器、pll、串口等

 

 


    platform_pre_init();    

        /* init timer */

        mtk_timer_init();        

         /* init pll */

        mt_pll_init();

        /* init uart baudrate when pll on */

        mtk_uart_init(UART_SRC_CLK_FRQ, CFG_LOG_BAUDRATE);

        /*GPIO init*/

        mt_gpio_init();

            mt_gpio_set_default(); //mtk GPIO默認配置

        clk_buf_all_on(); //打開clk buf

        #if (CFG_USB_UART_SWITCH) //如果設置了這個宏

if (is_uart_cable_inserted())  //插入了uart

set_to_uart_mode();  //切換到uart 模式

        //retry 3 times for pmic wrapper init,提高給其他模塊調用的API,這里使能一些狀態寄存器

        pwrap_init_preloader();

         //i2c hw init,初始化I2C的引腳,SDA,SCL

        i2c_hw_init();

         pmic_ret = pmic_init();  //另外單獨分析,proload電壓管理

        mt_pll_post_init(); //初始化pll

         //enable long press reboot function ,設置長按重啟

        PMIC_enable_long_press_reboot();

        platform_core_handler

            for (i = NR_CPUS - 1; i > 0; --i)

                spm_mtcmos_ctrl_cpu(i, STA_POWER_DOWN, 0); //關閉非啟動cpu

        ptp_init //初始化

 

1)mtk_timer_init()

    MT6577有7個GPT(General-Purpose Timer,通用計時/定時器),其中包括5個32位定時器和1個64位定時器。每個定時器有4種工作模式,分別是ONE-SHOT(一次使用的)、REPEAT(重復使用)、KEEP-GO(繼續使用)和FREERUN(自有運行的)。每個定時器工作的時鍾源可以是RTC時鍾(32.768kHz)或是系統時鍾(13MHz)。


 

圖11

此函數通過主要內容如下:

    1) 設置PERI_GLOBALCON_PDN0(C1000010,peripheral power-down 0 register for AP side)寄存器的GPT_PDN=1來給GPT上電。

    2) 清空和停止GPT4定時器

    通過設置GPT4_CON(C1002040)定制器來實現,代碼如下:

 
  1.     *GPT4_CON = 0x0; //disable  
  2.     *GPT4_CON = 0x2; //clear counter  
 
*GPT4_CON = 0x0; //disable
*GPT4_CON = 0x2; //clear counter

 

    3) 使能GPT4定時器

 
    1. //enable REN Bitfor GPT count error on free run mode  
    2. *GPT4_CLK =((GPT4_SYS_CLK)|GPT4_REN_CLK);  
    3. *GPT4_CON =(GPT4_EN|GPT4_FREERUN);  
//enable REN Bitfor GPT count error on free run mode
*GPT4_CLK =((GPT4_SYS_CLK)|GPT4_REN_CLK);
*GPT4_CON =(GPT4_EN|GPT4_FREERUN);

 

    4) 復位GPT4定時器

 

 

(2)platform_chip_ver()


 

圖12

    通過讀取對應寄存器的值來獲取chip ID、hardware version和software version。 

    這里我們讀出CHIP_SUBID=0X00008A00,相當於CHIP_6577_E1,根據MTK給出的說明:在上電后CPU 0和1可能沒有明確復位,需要手動復位,這通過設置RST_CTL0(MCUSYS復位控制寄存器0)的SW_CPU_RST位來實現:

 

 

 圖13

(3)mt6577_pll_init()

    這部分內容比較多,后續作為單獨一部分來介紹。

 

(4)mtk_uart_init(UART_SRC_CLK_FRQ,CFG_LOG_BAUDRATE)

    使用默認的時鍾源和921600波特率來初始化UART1。

 

(5)初始化PMIC的I2C接口和初始化PMIC(MT6329)

 

(6)根據DDR的類型,如果為DDR2或是DDR3則重新初始化PLL,那就需要重新初始化串口和I2C;如果為DDR1則不需要了。

 

 

 

3.1.2    platform_init()

     這里主要是平台初始化,包括看門狗初始化、根據啟動原因來決定是否給RTC和BBPU供電。

 
 
     /* check DDR-reserve mode */
    check_ddr_reserve_status(); //ddr的備用模式,我們沒有用
    /* init watch dog, will enable AP watch dog */
    mtk_wdt_init(); //初始化wdt
        mtk_wdt_check_status(); // This function will store the reset reason: Time out/ SW trigger 
    /*init kpd PMIC mode support*/
    set_kpd_pmic_mode();
        mtk_kpd_gpio_set();  //初始化鍵盤
    正常按power按鍵開機:BR_POWER_KEY插入USB開機:BR_USB;定時開機:BR_RTC ; 看門狗啟動:BR_WDT或者WDT_BY_PASS_PWK_REBOOT ; rtcl兩秒重啟:BR_2SEC_REBOOT
    g_boot_reason = reason = platform_boot_status(); //得到啟動原因
        if (reason == BR_RTC || reason == BR_POWER_KEY || reason == BR_USB || reason == BR_WDT || reason == BR_WDT_BY_PASS_PWK || reason == BR_2SEC_REBOOT)
            rtc_bbpu_power_on //設置一些RTC相關寄存器
                /* pull PWRBB high */
                bbpu = RTC_BBPU_KEY | RTC_BBPU_AUTO | RTC_BBPU_BBPU | RTC_BBPU_PWREN; 
    enable_PMIC_kpd_clock //使能kpd
    mt_mem_init(); //初始化內存
     /*init dram buffer*/
    init_dram_buffer(); //初始化DDR內存
    ram_console_init(); //控制台內存初始化
    ram_console_reboot_reason_save //保存reboot的原因
    /* init device storeage */
     ret = boot_device_init(); 
        mmc_init_device //初始化MMC
            mmc_init(MMC_HOST_ID, MSDC_MODE_DEFAULT);
            card = mmc_get_card(MMC_HOST_ID);
            .......一些初始化........
            mmc_addr_trans_tbl_init(card, &g_mmc_bdev);
            blkdev_register(&g_mmc_bdev);
    bootarg.dram_rank_num = get_dram_rank_nr();  //dram相關的設置
    get_dram_rank_size(bootarg.dram_rank_size);
    get_orig_dram_rank_info(&bootarg.orig_dram_info);
    setup_mblock_info(&bootarg.mblock_info, &bootarg.orig_dram_info, &bootarg.lca_reserved_mem);
              
  (1)bbpu指Basebandpower-up。

        當檢測到按下power按鍵或是USB/充電線插入,pre-loader調用rtc_bbpu_power_on()函數來鎖存RTC的PWBB來保持設備的一直供電,這樣就算是松開power按鍵設備也不會關機。

(2)platform_emergency_download()

    如果同時按下下面3個按鍵:

 
  1.     #define  KPD_DL_KEY1 8    /* KEY_POWER */  
  2.     #defineKPD_DL_KEY2  9    /* KEY_VOLUMEUP */  
  3.     #defineKPD_DL_KEY3  0    /* KEY_VOLUMEDOWN */  
#define  KPD_DL_KEY1 8    /* KEY_POWER */
#defineKPD_DL_KEY2  9    /* KEY_VOLUMEUP */
#defineKPD_DL_KEY3  0    /* KEY_VOLUMEDOWN */

 

    這3個除了power按鍵,其他2個應該是可以自定義的。

    Emergency DownloadMode緊急下載模式,它就是一個刷機模式,這里是同時按下這3個按鍵后進入緊急下載模式,但是不知道為什么就關機了,串口輸出信息如下:

 
  1. ……………  
  2. [PreLoader_mt6577_detect_powerkey]Press  
  3. power key ispressed  
  4. [PLFM] Power keyboot!  
  5. platform_init()g_boot_reason=0  
  6. Entermt6577_kpd_gpio_set!  
  7. [PreLoader_mt6577_detect_powerkey]Press  
  8. power key ispressed  
  9. download keys arepressed  
  10. [PLFM] emergencydownload mode(timeout: 300s).  
  11. mtk_arch_reset atpre-loader!  
  12. €€€€€€€€  
 
 
3.1.3     mode = seclib_brom_meta_mode();
  得到meta啟動模式,這里是訪問一個庫
 
 

3.1.4    uart_handshake_init()

META mode:MobileEngineering Test Architecture

if(mode == NORMAL_BOOT) //如果是NORMAL_BOOT

 

 uart_handshake_init();
 

 

    (1)切換到META端口

    mtk_serial_set_current_uart(CFG_UART_META);

    (2)判斷META和log端口是否一致,如果一致,就采用META的波特率來初始化META端口。並且同時關閉log,這樣log信息會保持在log緩沖區中。

    if (CFG_UART_META == CFG_UART_LOG) {

        /* to prevent sync error with PC */

   gpt_busy_wait_us(160);

   /* init to meta baudrate */

        mtk_uart_init(UART_SRC_CLK_FRQ, CFG_META_BAUDRATE);

        /* disable log so that log message will be kept in log buffer */

        log_buf_ctrl(0);

        log_ctrl(0);

    }

    (3)通過META端口發送ready給下載工具。

    uart_send((u8*)HSHK_COM_READY, strlen(HSHK_COM_READY));

    (4)切換回log端口。

    mtk_serial_set_current_uart(CFG_UART_LOG);
 
 
3.1.5 log_buf_ctrl(1); /* switch log buffer to dram */
  把log buffer方法DRAM里面,原來應該是在sram里面
 
 
3.1.6   part_init()
   
          主要是分區初始化 
        
 
 

3.1.7    part_dump()

    打印分區信息,包括分區名和分區占用的大小。

 

 

 

3.1.8  sec_lib_init()

    安全庫的初始化

 

 
 

3.2 bldr_handshake()

    通過USB或是UART和PC機運行的下載工具握手。

 

 

3.3 宇龍的ABOOT

 

    // add begin by wangping@yulong.com 20150710

 #ifdef YL_DOUBLE_ABOOT

    if (NULL == (bootdev = blkdev_get(CFG_BOOT_DEV))) {

        print("%s can't find boot device(%d)\n", MOD, CFG_BOOT_DEV);

        /* FIXME, should change to global error code */

        goto error;

    }

 

    recovery_flag = CFG_UBOOT_MEMADDR; // 閲囩敤DRAM鍦板潃錛屼笉閲囩敤SRAM鍦板潃錛堝爢鏍堬級闃叉鍫嗘爤婧㈠嚭

    part = part_get("yl_params");

    src = part->start_sect * bootdev->blksz;

    blkdev_read(bootdev, src, 512 * 4, (char*)recovery_flag, part->part_id);

    print("MutiBootloader=%x\n ", *((char*)recovery_flag + 0x6c5));

    boot_recovery = *((char*)recovery_flag + 0x6c5);

    // boot_recovery = 1; // for test

#endif

    // add end.

 

3.4.sec_boot_check(); 

 

/* security check */

 

3.5.device_APC_dom_setup

   建立動態控制modem網絡
 
 
3.6.trustzone_pre_init();
 安全區早期初始化
 
 
3.7.bldr_load_images(&jump_addr)
    加載Second Bootloader Load,uboot
 
 

3.8  bldr_post_process()

    preloader部分對平台部分的后處理

    platform_post_init

        platform_parse_bootopt //解析boot的選項,bootopt=
        platform_set_boot_args //設置boot的參數
            platform_set_boot_args_by_atag(&(g_dram_buf->boottag)); // set jump addr
            // Switch log port to UART2 while uart meta connected.
            if(g_boot_mode == META_BOOT && g_meta_com_type == META_UART_COM)
                mtk_serial_set_current_uart(UART2);
            
 
3.9.trustzone_post_init();
    安全控件初始化
 
 
3.10. bldr_jump(jump_addr, jump_arg, sizeof(boot_arg_t));

    跳轉到uboot起始地址處並執行。此時preloader的工作結束,轉入到uboot階段。

    jump_arg = (u32)&(g_dram_buf->boottag);前面會賦值
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM