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字节 |
分区表头(第0-91字节)的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