【Android】【MTK】MTK系统启动流程

MTK系统启动流程


启动流程图:

【Android】【MTK】MTK系统启动流程_第1张图片

一 BootRom
系统开机,最先执行的是固化在芯片内部的bootrom,其作用比较简单,主要有
a.初始化ISRAM和EMMC
b.当系统全擦后 ,也会配置USB,用来仿真USB端口下载镜像。
c.从EMMC中加载preloader到ISRAM中执行。


二 Preloader
preloader用来初始化外设,配置软件执行环境。
Preloader执行之前,外部Memory没有初始化,故Preloader在内部ISRAM中执行的,其会初始化外部Memory,这样以后的镜像数据就可以加载到外部Memory中来执行。
同时preloader还会初始化UART用来调试,进行META模式握手,配置USB用来下载镜像数据,查找PMT分区表,最后会根据PMT表从EMMC中加载lk/uboot到Memory中来执行。


我们系统有个PMT表,这个是个总的分区表,每个分区数据都可以从这个表中找到。
第一列是该分区起始地址,第二列是该分区占用多少个block,第三列是分区名字。

[0x0000000000000000-0x0000000000ffffff] (   32768 blocks): "PRELOADER"

[0x0000000001000000-0x000000000107ffff] (    1024 blocks): "MBR" 

[0x0000000001080000-0x00000000010fffff] (    1024 blocks): "EBR1" 

[0x0000000001100000-0x00000000013fffff] (    6144 blocks): "PRO_INFO" 

[0x0000000001400000-0x00000000018fffff] (   10240 blocks): "NVRAM" 

[0x0000000001900000-0x00000000022fffff] (   20480 blocks): "PROTECT_F" 

[0x0000000002300000-0x0000000002cfffff] (   20480 blocks): "PROTECT_S" 

[0x0000000002d00000-0x0000000002d1ffff] (     256 blocks): "SECURE" 

[0x0000000002d20000-0x0000000002d7ffff] (     768 blocks): "UBOOT" 

[0x0000000002d80000-0x000000000417ffff] (   40960 blocks): "BOOTIMG" 

[0x0000000004180000-0x000000000557ffff] (   40960 blocks): "RECOVERY" 

[0x0000000005580000-0x0000000005b7ffff] (   12288 blocks): "SECSTATIC" 

[0x0000000005b80000-0x0000000005bfffff] (    1024 blocks): "MISC" 

[0x0000000005c00000-0x0000000005efffff] (    6144 blocks): "LOGO" 

[0x0000000005f00000-0x0000000005f7ffff] (    1024 blocks): "EBR2" 

[0x0000000005f80000-0x0000000031b7ffff] ( 1433600 blocks): "CUSTPACK" 

[0x0000000031b80000-0x000000003237ffff] (   16384 blocks): "MOBILE_INFO" 

[0x0000000032380000-0x0000000032d7ffff] (   20480 blocks): "APANIC"

 [0x0000000032d80000-0x000000005537ffff] ( 1126400 blocks): "ANDSYSIMG" 

[0x0000000055380000-0x0000000061b7ffff] (  409600 blocks): "CACHE" 

[0x0000000061b80000-0x00000000a1b7ffff] ( 2097152 blocks): "USER"



三 LK(little kernel)
lk最主要的工作就是加载kernel和ramdisk,然后跳转到kernel中去执行。
同时有几个比较重要工作也是在lk中执行
a.初始化LCD,加载并显示开机logo。
b.对启动模式判断,meta模式,recovery模式,power off charging模式,fastboot模式等等。
c.fastboot也是在lk中实现的,主要作用就是下载我们手机镜像。


LK入口在kmain,它是由crt0.s连入的。
void kmain(void)  //bootable/bootloader/lk/kernel/main.c 
kmain中主要关注两个下面几个初始化操作 
platform_early_init()  //mediatek/platform/mt6582/lk/platform.c 
platform_init();//mediatek/platform/mt6582/lk/platform.c 
apps_init(); 


1)apps_init原理
 

【Android】【MTK】MTK系统启动流程_第2张图片
apps_init会依次执行__apps_start与__apps_end之间所有的init函数,__apps_start与__apps_end的定义在如下连接脚本中。


bootable/bootloader/lk/arch/arm/system-onesegment.ld

【Android】【MTK】MTK系统启动流程_第3张图片

 
意思是__apps_start跟__apps_end是.apps section的开始和结尾地址,属于.apps section的数据会依次存放在__apps_start跟__apps_end之间。 


见如下示例: 
APP_START(mt_boot) 
     .init = mt_boot_init, 
APP_END 


APP_START(aboot) 
    .init = aboot_init, 
APP_END 


#define APP_START(appname) struct app_descriptor _app_##appname __SECTION(".apps") = { .name = #appname, 
#define APP_END }; 


对mt_boot和aboot分别展开宏定义: 
struct app_descriptor _app_mt_boot __SECTION(".apps") = 

.init=mt_boot_init, 

struct app_descriptor _app_aboot __SECTION(".apps") = 

.init=aboot_init, 
}
可以看到_app_mt_boot和_app_aboot定义的结尾有__SECTION(".apps") ,系统编译连接的时候就会把这两个变量放到.apps section中。


这样我们通过依次遍历__apps_start跟__apps_end就可以找到所有init函数。
这样方式在kernel中用的非常多。


2)启动模式
请参考boot_mode_select函数,mediatek/platform/mt6582/lk/boot_mode.c
进入fastboot一般有两种方式,一是通过开机并按键进入,二是通过adb reboot bootloader命令进入。
a)按键进入就是开机过程中通过按某个键来进入fastboot模式,这个按键是需要在lk配置的,目前有些项目是没有配置的。


b)通过adb命令进入,运行adb reboot bootloader系统会重启,下次开机会自动进入fastboot模式。其原理是执行adb reboot bootloader后,系统是会写一个fastboot标志位到RTC寄存器,下次开机运行到lk中,会在boot_mode_select函数中调用Check_RTC_PDN1_bit13()来检测是否进入fastboot模式。 
Fastboot相关代码请参考 
bootable/bootloader/lk/app/aboot/fastboot.c 


3)lk加载解析bootimage头,加载kernel和ramdisk. 
主要相关代码在boot_linux_from_mmc()
首先就是通过PMT表找到bootimage的分区起始地址,然后从分区指定地址开始读取bootimage的头,
struct boot_img_hdr
{
    unsigned char magic[BOOT_MAGIC_SIZE];
    unsigned kernel_size;  /* size in bytes */
    unsigned kernel_addr;  /* physical load addr */
    unsigned ramdisk_size; /* size in bytes */
    unsigned ramdisk_addr; /* physical load addr */
    unsigned second_size;  /* size in bytes */
    unsigned second_addr;  /* physical load addr */
    unsigned tags_addr;    /* physical addr for kernel tags */
    unsigned page_size;    /* flash page size we assume */
    unsigned unused[2];    /* future expansion: should be 0 */
    unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */
    unsigned char cmdline[BOOT_ARGS_SIZE];
    unsigned id[8]; /* timestamp / checksum / sha1 / etc */
};


/*
** +-----------------+ 
** | boot header     | 1 page
** +-----------------+
** | kernel          | n pages  
** +-----------------+
** | ramdisk         | m pages  
** +-----------------+
** | second stage    | o pages
** +-----------------+


bootimage的头中包含kernel加载的物理内存地址,kernel大小,ramdisk加载的物理内存地址,ramdisk大小,同时还有atag对应的物理地址等待,解析完bootimage头,就可以根据以上信息来从EMMC中加载kernel和ramdisk到指定的内存地址上


memmove((void*) hdr->kernel_addr, (char *)(image_addr + page_size), hdr->kernel_size); 


memmove((void*) hdr->ramdisk_addr, (char *)(image_addr + page_size + kernel_actual), hdr->ramdisk_size); 


lk传递参数到内核中可以通过atag传递,cmdline其实也是atag中的某一项。配置完atag后,就可以跳转到kernel入口去执行


boot_linux((void *)hdr->kernel_addr, (unsigned *) hdr->tags_addr, (const char *)cmdline, board_machtype(), (void *)hdr->ramdisk_addr, hdr->ramdisk_size);
{
unsigned *ptr = hdr->tags_addr; 
if (ramdisk_size) { 
*ptr++ = 4;    //atag这项长度
*ptr++ = 0x54420005;   //标示这项atag是ramdisk,#define ATAG_INITRD2 0x54420005
*ptr++ = (unsigned)hdr->ramdisk_addr; //ramdisk的物理地址
*ptr++ = hdr->ramdisk_size;   //ramdisk大小
}
hdr->kernel_addr(0, machtype, hdr->tags_addr); //执行kernel入口
}


四 kernel及init启动
kernel C的入口是start_kernel,从head-common.S汇编跳转过来。
具体kernel启动过程,可以在网上参考文档,这里说下对atag的解析和init进程启动


kernel对atag的解析是在kernel/arch/arm/kernel/setup.c中


static int __init parse_tag(const struct tag *tag) 

extern struct tagtable __tagtable_begin, __tagtable_end; 
struct tagtable *t; 


for (t = &__tagtable_begin; t < &__tagtable_end; t++) 
if (tag->hdr.tag == t->tag) { 
t->parse(tag); 
break; 



return t < &__tagtable_end; 



__tagtable_begin跟 __tagtable_end,其实也是在连接脚本中定义的,
kernel/arch/arm/kernel/vmlinux.lds.S
__tagtable_begin = .; 
*(.taglist.init) 
__tagtable_end = .;
表示.taglist.init section开始和结尾。
下面的__tagtable()就会把数据放到.taglist.init section。
kernel/arch/arm/include/asm/setup.h 


__tagtable(ATAG_CORE, parse_tag_core); 
__tagtable(ATAG_MEM, parse_tag_mem32); 
__tagtable(ATAG_MEM64, parse_tag_mem64); 
__tagtable(ATAG_VIDEOTEXT, parse_tag_videotext); 
__tagtable(ATAG_SERIAL, parse_tag_serialnr); 
__tagtable(ATAG_REVISION, parse_tag_revision); 
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
__tagtable(ATAG_INITRD2, parse_tag_initrd2); 


#define ATAG_INITRD2 0x54420005
static int __init parse_tag_initrd2(const struct tag *tag) 

phys_initrd_start = tag->u.initrd.start; 
phys_initrd_size = tag->u.initrd.size; 
return 0; 

当系统解析到ATAG_INITRD2(0x54420005)这项atag时,就会执行 parse_tag_initrd2函数,从atag中取得ramdisk内存地址和大小。


init进程启动
start_kernel---->rest_init();


kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);


static int __init kernel_init(void * unused)
{
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";


init_post();
}


static noinline int init_post(void)
{
if (ramdisk_execute_command) {
run_init_process(ramdisk_execute_command);
printk(KERN_WARNING "Failed to execute %s\n",
ramdisk_execute_command);
}


if (execute_command) {
run_init_process(execute_command);
printk(KERN_WARNING "Failed to execute %s.  Attempting "
"defaults...\n", execute_command);
}
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");


}
最终会执行我们系统中的init进程,依次从如下地方查找init进程,/init,/sbin/init,/etc/init,/bin/init,/bin/sh。




init主要处理


1)创建系统相关目录并挂在系统相关文件系统。
    mkdir("/dev", 0755);
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);


    mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
    mkdir("/dev/pts", 0755);
    mkdir("/dev/socket", 0755);
    mount("devpts", "/dev/pts", "devpts", 0, NULL);
    mount("proc", "/proc", "proc", 0, NULL);
    mount("sysfs", "/sys", "sysfs", 0, NULL);


2)初始化属性系统,并加载解析默认属性文件/build.prop
property_init();
 property_load_boot_defaults();


3)解析相关rc文件。
 init_parse_config_file("/init.rc");
 init_parse_config_file("/init.project.rc");


rc文件包含四种类型语句:Actions, Commands, Services, Options。 




Actions 
Actions其实就是一组被命名的Commands序列。当满足触发器的事件发生时,这个action就会被置于一个队列中,这个队列存放着将要被执行的action。其格式如下: 
    on  
           
           
    on是Actions的关键字,它表明下面的序列是Actions序列。 


Services 
    Services是有init进程启动的或者重新启动的程序。其格式如下: 
    service [
         

你可能感兴趣的:(System)