MSM8909+Android5.1.1启动流程(4)---bootstrap2()

MSM8909+Android5.1.1启动流程(4)---bootstrap2()

备注:

LBA:logic blockaddress,逻辑块地址

static int bootstrap2(void *arg)
{
       dprintf(SPEW,"top of bootstrap2()\n");
 
       arch_init();
 
       //initialize the rest of the platform
       dprintf(SPEW,"initializing platform\n");
       platform_init();
 
       //initialize the target
       dprintf(SPEW,"initializing target\n");
       target_init();
 
       dprintf(SPEW,"calling apps_init()\n");
       apps_init();
 
       return0;
}

arch_init()和platform_init()都是空函数

 

1.     target_init()

 

1.1  spmi_init()

初始化SPMI(System Power Management Interface)总线控制器。

1.2  target_keystatus()

(1)  调用keys_init()初始化全局数据key_bitmap[]为0,此数据记录对应的按键是否被按键。

(2)  调用target_volume_down()和target_volume_up()函数判断音量+-按键是否按下,如果按下,就设置key_bitmap[]对应的位为1,比如key_bitmap[KEY_VOLUMEDOWN]=1,这为后面进入线刷、recovery、还是正常启动模式做好判断依据。

 

这里音量+-是根据GPIO_90和GPIO_91的引脚来判断的。

 

1.3  target_sdc_init()

调用platform_boot_dev_isemmc()判断启动设备是否是EMMC,如果是就调用target_sdc_init()来初始化SDC(Secure digital controller)控制器接口

Sdc:Secure digital controllerinterface-supports EMMC


1.4  partition_read_table()

先读取MMC的MBR分区,如果是PMBR分区就读取GPT分区信息。

MBR(Master Boot Record)和GPT(GUID Partition Table).

 

看代码之前先看《MBR和GPT概要学习》部分,链接:

http://blog.csdn.net/loongembedded/article/details/51763187

 

1.4.1       mmc_get_device_blocksize()

获取EMMC的块大小,为512个字节。

1.4.2       mmc_boot_read_mbr()

读取EMMC的MBR

 

(1)   调用mmc_read()从0地址开始读取512字节的数据到buff中。

(2)   partition_verify_mbr_signature()对读取出来数据的进行MBR签名核查。

如果buff[510]不等于0x55,或是buff[511]不等于0xAA,就说明MBR signature不匹配

(3)   在MBR中读取4个分区信息到我们的MBR表

EMMC的MBR由4部分组成,这里是要读分区表信息,其中包含4个分区项,偏移地址是01BEH--01FDH,每个分区表项长16个字节,共64字节为分区项1、分区项2、分区项3、分区项4

 

读取每个16字节的分区项时,先读取第5个字节对应的分区类型,如果为0xEE 表示该分区表是PMBR,紧随其后的应该是GPT分区表头和GPT分区表,因此这是一块GPT硬盘。

 

这里读取出来的值就是0xEE,表示此MMC采用GPT分区结构,如果不是,就按照MBR分区结构读取后面的分区信息

 

1.4.3       mmc_boot_read_gpt(),从MMC中读取GPT信息并填充分区表

GPT分区全名为GloballyUnique Identifier Partition Table Format,即全局唯一标示磁盘分区表格式

 

(1)  mmc_get_device_capacity()获取MMC设备的密度,其值为1

(2)  mmc_read()读取第1个扇区内容(第0个扇区是PMBR)。

(3)  partition_parse_gpt_header()解析GPT分区表头,如果解析失败再去读取备份GPT分区表头

 

先读取和解析GPT分区表头,因为GPT分区表信息没有被破坏,就没有去读取备份分区表头信息。

1)     判断GPT头的大小,92个字节。

2)     计算GPT头的CRC并和之前写入的GPT头的CRC对比。

3)     读取GPT头的LBA和分区表项值

具体信息如下:

偏移地址24:当前的LBA(这个GPT头的位),这里为1

偏移地址40:第一个可用于分区的LBA(主分区表的最后一个LBA+1)

偏移地址48:最有一个可用于分区的LBA(备份分区表的第一个LBA-1)

偏移地址80:分区表项的数量,这里为32个

偏移地址84:一个分区表项的大小(通常为128),这里为128

分区表头的格式

起始字节

长度

内容

0

8字节

签名("EFI PART"

8

4字节

修订

12

4字节

分区表头的大小

16

4字节

分区表头(第091字节)的CRC32校验,在计算时,把这个字段作为0处理,需要计算出分区串行的CRC32校验后再计算本字段

20

4字节

保留,必须是 0

24

8字节

当前LBA(这个分区表头的位置)

32

8字节

备份LBA(另一个分区表头的位置)

40

8字节

第一个可用于分区的LBA(主分区表的最后一个LBA + 1

48

8字节

最后一个可用于分区的LBA(备份分区表的第一个LBA − 1

56

16字节

硬盘GUID(在UNIX系统中也叫UUID

72

8字节

分区表项的起始LBA(在主分区表中是2

80

4字节

分区表项的数量

84

4字节

一个分区表项的大小(通常是128

88

4字节

分区串行的CRC32校验

92

*

保留,剩余的字节必须是0(对于512字节LBA的硬盘即是420个字节)

 

 

(4)  读取GPT分区表中的分区信息,从第2~9个block读取,然后写入到记录分区信息的全局数组中partition_entrie[]

 

每个entry(分区表项)占128字节,所以一个block包含4个分区表项,而我们分区表项数量是32个,需要32/4=8个block。

 

GPT分区表项的格式

起始字节

长度

内容

0

16字节

分区类型GUID

16

16字节

分区GUID

32

8字节

起始LBA(小端格式)

40

8字节

末尾LBA

48

8字节

属性标签(如:60表示"只读")

56

72字节

分区名(可以包括36个UTF-16(小端格式)字符)

 

对应的结构体定义如下

/* Unified mbr and gpt entry types */
struct partition_entry {
       unsignedchar type_guid[PARTITION_TYPE_GUID_SIZE];
       unsigneddtype;
       unsignedchar unique_partition_guid[UNIQUE_PARTITION_GUID_SIZE];
       unsignedlong long first_lba;
       unsignedlong long last_lba;
       unsignedlong long size;
       unsignedlong long attribute_flag;
       unsignedchar name[MAX_GPT_NAME_SIZE];
       uint8_tlun;
};

到此partition_entry关于GPT分区表项的信息已获知。

 

1.5  shutdown_detect()

#if LONG_PRESS_POWER_ON
              shutdown_detect();
#endif

如果开启长按power按键开机(大概2~3s)机制,就会调用此函数,如果按power按键时间短于这个时间,就会关机。

 

1.6  target_use_signed_kernel()

if (target_use_signed_kernel())
              target_crypto_init_params();

target_use_signed_kernel()函数在bootloader/lk/target/init.c实现

__WEAK bool target_use_signed_kernel(void)
{
#if _SIGNED_KERNEL
       return1;
#else
       return0;
#endif
}

没有定义_SIGNED_KERNEL,所以直接返回0,也就没调用target_crypto_init_params()。

 

1.7  rpm_smd_init()

#if SMD_SUPPORT
       rpm_smd_init();
#endif

rpm_smd_init()中的RPM是指Cortex-M3,ResourcePower Manager资源功耗管理子系统,smd指Shared Memory Driver。

 

2.     apps_init()

/* one time setup */
void apps_init(void)
{
       conststruct app_descriptor *app;
 
       /*call all the init routines */
/* 遍历所有在__apps_start 到__apps_end段里的函数,并调用其init函数 */
    /* __apps_start 和__apps_end包含的段的段名在下面这个文件里指定为.apps段
     *   arch/arm/system-twosegment.ld
     *    在下面的文件里定义了一个宏来指定怎么样的函数放到上面的段中去,即被该红修饰的函数
     *   include/app.h
     * #define APP_START(appname)struct app_descriptor _app_##appname __SECTION(".apps") = { 
     *    .name =#appname,
     * #define APP_END};    
     */
       for(app = &__apps_start; app != &__apps_end; app++) {
              if(app->init)
                     app->init(app);
       }
 
       /*start any that want to start on boot */
       for(app = &__apps_start; app != &__apps_end; app++) {
              if(app->entry && (app->flags & APP_FLAG_DONT_START_ON_BOOT) ==0) {
                     start_app(app);
              }
       }
}

遍历所有在__apps_start 到__apps_end段里的函数,并调用其init函数和开始这个app,这部分后面作为单独部分学习。

 

参考链接:

解析NTFS(一) MBR部分

http://www.pediy.com/kssd/pediy11/112459.html

 

MBR&GPT硬盘分区类型&属性详解(Win下更改/设置OEM/恢复分区方法)

http://www.iruanmi.com/mbr-and-gpt-partition-type-and-attributes/

 

UEFI+GPT引导基础篇(一):什么是GPT,什么是UEFI?

http://www.iruanmi.com/what-is-gpt-and-what-is-uefi/

 

全局唯一标识分区表

http://baike.baidu.com/link?url=2y6fiXyz5oFG2IVecExQBbQbwl0ms4B1BTcb6EOewB6rBc8UxMoo7p26U2W0SLuvGFpONd6UEyzzauk3TNZnOK

 

你可能感兴趣的:(MSM8909+Android5.1.1启动流程(4)---bootstrap2())