Android开发之----proloader

来源:互联网 发布:泡妞的软件排行 编辑:程序博客网 时间:2024/05/02 01:21

preloader按照mtk的说法是MTK in-house developed loader,也就说是mtk内部开发的一个loader,那么单独编译preloader也是可以的,使用命令./mk project_name n pl。
1. 启动流程
首先需要明确的是preloader、lk、kernel、android这些系统镜像文件是存储在nand flash中的,然后每个mtk芯片都有个boot rom,在上电时刻,boot rom开始启动,boot rom加载preloader到内部的SRAM中,为什么是加载到内部的SRAM中,而不是外部RAM中呢,是因为这个时候外部RAM还没有被初始化好,preloader被加载完成之后,程序就从boot rom跳转到preloader处开始执行,preloader初始化好外部RAM之后,preloader将lk(或uboot)加载外部RAM中,然后跳转到lk(或uboot)中去执行,lk(或uboot)紧接着就加载bootimage(包括kernel和ramdisk)到外部RAM中,然后去执行kernel部分。启动过程如图所示:

2. 下载流程
上面是启动流程,preloader除了具有启动功能之外,他还具有下载功能。首先还是需要明确的是mtk芯片都的有个boot rom,如果没有这个rom那么,那么程序是无法被下载到nand flash中的,然后此时的flash上是为空的,没有任何数据的。系统在上电之后它会检测是启动模式还是下载模式,如果是下载模式,它会初始化一个usb的串口,将preloader加载到内部的SRAM中,跳转到preloader中去执行,初始化好flash和外部RAM之后,依次将preloader、lk、kernel、android下载到nand flash中去。下载过程如图所示:

3. 代码组织
preloader主要有三处代码:
1. mediatek/platform/mt6582/preloader
2. mediatek/custom/mt6582/preloader
3. mediatek/custom/hexing82_wet_jb5/preloader
那么同lk类似,在执行custgen过程中,会将custom目录下的内容生成到out目录中,所以参与preloader编译过程的之后两个目录:
1. mediatek/platform/mt6582/preloader
2. mediatek/custom/out/hexing82_wet_jb5/preloader
编译完成之后在out目录下生成preloader_project_name.bin
注意在mediatek目录下还有个preloader目录,这个目录下主要是一些编译脚本和makefile,最后也会在该目录下生成最终的preloader的bin文件。

 

 

 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------

先来看MT6577内存映射图:


图1

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. 基于MT6577+Android4.04的启动流程

 

先来看启动流程图:


 

 

图2

正常启动的主要工作如下:

(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内核并执行。

 

2. Pre-loader的过程(procedure)和流程(flow)

如下图:


图3

具体的源代码后面再分析了。

 

3. Pre-loader启动过程的分析

 

3.1 resethandler()

这是pre-loader的入口函数,在mediatek\platform\mt6577\preloader\src\init\init.s中定义,下面类分下此函数的代码:


[plain] view plaincopyprint?在CODE上查看代码片派生到我的代码片
resethandler :  
    MOV r0, #0  
    MOV r1, #0  
    MOV r2, #0  
    MOV r3, #0  
    MOV r4, #0  
    MOV r5, #0  
    MOV r6, #0  
    MOV r7, #0  
    MOV r8, #0  
    MOV r9, #0  
    MOV r10, #0  
    MOV r11, #0  
    MOV r12, #0  
    MOV sp, #0  
    MOV lr, #0  

resethandler :    MOV r0, #0    MOV r1, #0    MOV r2, #0    MOV r3, #0    MOV r4, #0    MOV r5, #0    MOV r6, #0    MOV r7, #0    MOV r8, #0    MOV r9, #0    MOV r10, #0    MOV r11, #0    MOV r12, #0    MOV sp, #0    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 plaincopyprint?在CODE上查看代码片派生到我的代码片
/* set the cpu toSVC32 mode */  
    MRS    r0,cpsr  
    BIC    r0,r0,#0x1f  
    ORR    r0,r0,#0xd3  
    MSR    cpsr,r0  
   
    /* disable interrupt */  
    MRS r0, cpsr  
    MOV r1, #INT_BIT  
    ORR r0, r0, r1  
MSR cpsr_cxsf, r0     

/* set the cpu toSVC32 mode */    MRS    r0,cpsr    BIC    r0,r0,#0x1f    ORR    r0,r0,#0xd3    MSR    cpsr,r0     /* disable interrupt */    MRS r0, cpsr    MOV r1, #INT_BIT    ORR r0, r0, r1MSR 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表示管理模式。

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



[plain] view plaincopyprint?在CODE上查看代码片派生到我的代码片
/* enable I+Z bits */  
MRC p15, 0, ip, c1, c0, 0 /*read SCTLR*/  
ORR ip, ip, #0x1800   /* I+Z bits */  
p15, 0, ip, c1, c0, 0  /*write SCTLR*/  

    /* enable I+Z bits */    MRC p15, 0, ip, c1, c0, 0 /*read SCTLR*/    ORR ip, ip, #0x1800   /* I+Z bits */MCR 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 plaincopyprint?在CODE上查看代码片派生到我的代码片
ORR ip, ip, #0x1800  
MCR p15, 0, ip, c1,c0, 0  

ORR ip, ip, #0x1800MCR p15, 0, ip, c1,c0, 0

由此可见是要设置SCTLR[12:11]=0b11,我们给出这两位的说明:


图7

到此我们就可以理解上面代码的意义了。

 

 

 

3.2 Main()

mediatek\platform\mt6577\preloader\src\core\main.c

main()函数的调用流程如下:



图8

 

这里要特别注意就是不能在调用bldr_pre_process()之前调用串口输出信息的函数,比如print(),否则无法启动,而且还无法再次烧录。上面这些函数都是以bldr开头,这是bootloader的简称。

 

下面来学习这几个函数的主要功能:

3.2.1  bldr_pre_process()


图9

 

3.2.1.1    platform_pre_init()

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


图10

(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)定制器来实现,代码如下:


[plain] view plaincopyprint?在CODE上查看代码片派生到我的代码片
*GPT4_CON = 0x0; //disable  
*GPT4_CON = 0x2; //clear counter  

*GPT4_CON = 0x0; //disable*GPT4_CON = 0x2; //clear counter

3) 使能GPT4定时器


[plain] view plaincopyprint?在CODE上查看代码片派生到我的代码片
//enable REN Bitfor GPT count error on free run mode  
*GPT4_CLK =((GPT4_SYS_CLK)|GPT4_REN_CLK);  
*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.2.1.2    platform_init()

这里主要是平台初始化,包括看门狗初始化、根据启动原因来决定是否给RTC和BBPU供电。


图14

正常按power按键开机:BR_POWER_KEY

插入USB开机:BR_USB

定时开机:BR_RTC

 

(1)bbpu指Basebandpower-up。

当检测到按下power按键或是USB/充电线插入,pre-loader调用rtc_bbpu_power_on()函数来锁存RTC的PWBB来保持设备的一直供电,这样就算是松开power按键设备也不会关机。

(2)platform_emergency_download()

如果同时按下下面3个按键:


[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
#define  KPD_DL_KEY1 8    /* KEY_POWER */   
#defineKPD_DL_KEY2  9    /* KEY_VOLUMEUP */   
#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个按键后进入紧急下载模式,但是不知道为什么就关机了,串口输出信息如下:


[plain] view plaincopyprint?在CODE上查看代码片派生到我的代码片
……………  
[PreLoader_mt6577_detect_powerkey]Press  
power key ispressed  
[PLFM] Power keyboot!  
platform_init()g_boot_reason=0  
Entermt6577_kpd_gpio_set!  
[PreLoader_mt6577_detect_powerkey]Press  
power key ispressed  
download keys arepressed  
[PLFM] emergencydownload mode(timeout: 300s).  
mtk_arch_reset atpre-loader!  
????????  

……………[PreLoader_mt6577_detect_powerkey]Presspower key ispressed[PLFM] Power keyboot!platform_init()g_boot_reason=0Entermt6577_kpd_gpio_set![PreLoader_mt6577_detect_powerkey]Presspower key ispresseddownload keys arepressed[PLFM] emergencydownload mode(timeout: 300s).mtk_arch_reset atpre-loader!????????


 

3.2.1.3    uart_handshake_init()

META mode:MobileEngineering Test Architecture

(1)切换到META端口

(2)判断META和log端口是否一致,如果一致,就采用META的波特率来初始化META端口。并且同时关闭log,这样log信息会保持在log缓冲区中。

(3)通过META端口发送ready给下载工具。

(4)切换回log端口。

 

3.2.1.4    part_init()

主要是分区初始化

3.2.1.5    part_dump()

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

3.2.1.6    sec_lib_init()

安全库的初始化

3.2.2  bldr_handshake()

通过USB或是UART和PC机运行的下载工具握手。

 

3.2.3  bldr_load_part()

加载Uboot。

3.2.4  bldr_post_process()

preloader部分对平台部分的后处理。

3.2.5  bldr_jump()

跳转到uboot起始地址处并执行。此时preloader的工作结束,转入到uboot阶段。


0 0