原文链接:http://www.codingblog.cn/blog/44451.html
注:很多内容和MTK LK阶段相同,可以借鉴学习。aboot.c内容差异很大。
本文以C6的bootlader代码为例,
1. 一 kmain
1.1. 1 crt0.S
手机上电后,硬件会从固定的地址(固化在ROM中)加载bootloader到RAM,然后跳转到bootloader的入口函数开始执行,对于mido,它的入口函数是:
source
#define
byte
0x4f
,
0xf0
,
0x7f
,
0xf5
#define
byte
0x6f
,
0xf0
,
0x7f
,
0xf5
.section
".text.boot"
.globl
_start:
b
b
b
b
.....
.....
.....
#ifdef
DSB
ISB
#endif
bl
b
|
在_start中先主要完成CPU初始化,禁用mmu,禁用cache,初始化异常向量表等操作,最后将直接跳转到函数kmain中
1.2. 2 kmain
kmain的代码位于
source
/*
void
kmain(
void
)
void
kmain(
void
)
{
//
thread_init_early();
//
arch_early_init();
//
platform_early_init();
//
target_early_init();
dprintf(INFO,
"welcome
);
bs_set_timestamp(BS_BL_START);
//
dprintf(SPEW,
"calling
);
call_constructors();
//
dprintf(SPEW,
"initializing
);
heap_init();
__stack_chk_guard_setup();
//
dprintf(SPEW,
"initializing
);
thread_init();
//
dprintf(SPEW,
"initializing
);
dpc_init();
//
dprintf(SPEW,
"initializing
);
timer_init();
#if
//
dprintf(SPEW,
"creating
);
thread_resume(thread_create(
"bootstrap2"
,
//
exit_critical_section();
//
thread_become_idle();
#else
bootstrap_nandwrite();
#endif
}
|
在kmain中,
2.1 调用thread_init_early初始化线程系统
2.2 调用arch_early_init中判断如果存在mmu就初始化,设置异常向量基地址,使能中断相关寄存器
2.3 在platform_early_init中完成初始化硬件时钟、手机的主板等操作,这个函数每种cpu的实现都不一样,定义在bootable\bootloader\lk\platform\{cpu型号}\platform.c下
2.4 target_early_init中完成初始化uart端口的操作,这个函数的实现在bootable\bootloader\lk\target\{cpu型号}\init.c
2.5 首先输出调试信息”welcome to lk\n\n”,在bootable/bootloader/lk/include/debug.h中将INFO定义为了类似kernel中使用的debug
level,同时定义了dprintf
source
44
/*
45#define
46#define
47#define
48#define
49
50
/*
51void
char
c);
//
52int
const
char
*str);
53int
const
char
*fmt,
54int
const
char
*fmt,
va_list
ap);
55
56#define
do
{
if
((level)
while
(0)
57#define
do
{
if
((level)
while
(0)
58#define
do
{
if
((level)
while
(0)
59#define
do
{
if
((level)
while
(0)
|
然后调用bs_set_timestamp设置启动状态为BS_BL_START,启动状态的其他值定义在bootable/bootloader/lk/platform/msm_shared/include/boot_stats.h中:
source
33
/*
34
35
36
37enum
38
39
40
41
42
43
44
45};
|
2.6 SPEW同样是在bootable/bootloader/lk/include/debug.h中定义的debug
level,函数call_constructors完成相关构造函数的初始化。
2.7 调用函数heap_init完成内核堆栈的初始化,用与kmalloc等函数的内存分配。
2.8 __stack_chk_guard_setup()其实是个宏,定义在bootable/bootloader/lk/include/debug.h中,
#define __stack_chk_guard_setup() do { __stack_chk_guard = get_canary(); } while(0)
get_canary函数定义在bootable/bootloader/lk/platform/msm_shared/scm.c中,返回一个生成的随机数,最终保存在全局变量__stack_chk_guard中。
2.9 在thread_init函数中初始化定时器
2.10 初始化延迟过程调用(delay procedure call)
2.11 调用timer_init初始化内核定时器
2.12 如果没有定义ENABLE_NANDWRITE,就创建出一个名为bootstrap2的线程,然后运行这个线程。退出临界区,开中断。全局变量critical_section_count在定义时取值为1,在exit_critical_section中会将critical_section_count先减1,如果减一后为0就使能中断。
source
136static
inline
__ALWAYS_INLINE
void
exit_critical_section(
void
)
137{
138
139
if
(critical_section_count
140
141}
|
最后调用thread_become_idle将本线程切换到idle状态
2.13 如果定义了ENABLE_NANDWRITE,在timer_init之后将执行bootstrap_nandwrite。我们接下来先以没有定义ENABLE_NANDWRITE来进行分析。
2. 二 bootstrap2
bootstrap2线程会执行bootstrap2函数
source
static
int
bootstrap2(
void
*arg)
{
dprintf(SPEW,
"top
);
arch_init();
//
#if
bio_init();
#endif
#if
fs_init();
#endif
//
dprintf(SPEW,
"initializing
);
platform_init();
//
dprintf(SPEW,
"initializing
);
target_init();
dprintf(SPEW,
"calling
);
apps_init();
return
0;
}
|
2.1. 1 arch_init
arch_init函数目前实现为空。如果定义了WITH_LIB_BIO和WITH_LIB_FS,就分别执行bio_init和fs_init。函数platform_init的实现也跟cpu关联,但基本都是输出调试信息。
2.2. 2 target_init
target_init的实现也与cpu型号有关,对于mido,其cpu型号为msm8953,target_init的实现为:
source
void
target_init(
void
)
{
#if
#if
int
ret
#endif
#endif
dprintf(INFO,
"target_init()\n"
);
spmi_init(PMIC_ARB_CHANNEL_NUM,
target_keystatus();
target_sdc_init();
if
(partition_read_table())
{
dprintf(CRITICAL,
"Error
);
ASSERT(0);
}
#if
shutdown_detect();
#endif
#if
vib_timed_turn_on(VIBRATE_TIME);
#endif
if
(target_use_signed_kernel())
target_crypto_init_params();
#if
#if
clock_ce_enable(CE1_INSTANCE);
/*
ret
if
(ret
{
dprintf(CRITICAL,
"Failed
,
ASSERT(0);
}
/*
ret
if
(ret
{
dprintf(CRITICAL,
"Failed
,
ASSERT(0);
}
if
(rpmb_init()
{
dprintf(CRITICAL,
"RPMB
);
ASSERT(0);
}
/*
*
*/
if
(load_sec_app()
{
dprintf(CRITICAL,
"Failed
);
ASSERT(0);
}
#endif
#endif
#if
rpm_smd_init();
#endif
}
|
主要完成的操作有:
2.1从共享内存中读取xbl提供的pmic信息(pmic_info_populate)
2.2初始化spmi总线,用于cpu与pmic通信(spmi_init)
2.3初始化ap与rpm通信通道(rpm_glink_init)
2.4初始化按键(target_keystatus)
2.5判断内核是否签名,当使用的是签名的kernel时,需要初始加密解密引擎(target_crypto_init_params)
2.6判断是从usf还是emmc启动(platform_boot_dev_isemmc)
2.7获取分区表信息(mmc_read_partition_table)
2.8判断电池电压是否过低,过低则进入预充电(pm_appsbl_chg_check_weak_battery_status)
2.9和tz通信(qseecom_tz_init)
2.10初始化emmc或ufs中的rpmb用户加解密认证分区(rpmb_init)
2.11运行keymaster(load_sec_app)
3. 三 apps_init
在bootstrap2线程中最后执行apps_init,完成一些应用功能的初始化。
3.1. 1 apps_init
apps_init的实现在:
source
/*
void
apps_init(
void
)
{
const
struct
app_descriptor
/*
for
(app
if
(app->init)
app->init(app);
}
/*
for
(app
if
(app->entry
start_app(app);
}
}
}
static
int
app_thread_entry(
void
*arg)
{
const
struct
app_descriptor
const
struct
app_descriptor
app->entry(app,
return
0;
}
static
void
start_app(
const
struct
app_descriptor
{
thread_t
printf
(
"starting
,
thr
void
*)app,
if
(!thr)
{
return
;
}
thread_resume(thr);
}
|
__apps_start和__apps_end都是在连接脚本中定义的, 在 bootable/bootloader/lk/arch/arm/system-onesegment.ld中有:
__apps_start = .;
KEEP (*(.apps))
__apps_end = .;
__apps_start和__apps_end都代表两个链接地址,KEEP (*(.apps))表示所有.apps段都链接在__apps_start和__apps_end之间。
因为在bootable/bootloader/lk/include/app.h中有定义
source
/*
void
apps_init(
void
);
/*
/*
struct
app_descriptor;
typedef
void
(*app_init)(
const
struct
app_descriptor
typedef
void
(*app_entry)(
const
struct
app_descriptor
void
*args);
/*
#define
/*
struct
app_descriptor
const
char
*name;
app_init
app_entry
unsigned
int
flags;
};
#define
#define
|
所以这里将会执行在APP_START(appname)中定义的appname函数。在代码里搜索APP_START,会发现在bootable/bootloader/lk/app/aboot/aboot.c中调用APP_START来执行aboot_init函数
3.2. 2 aboot_init
aboot_init的定义为:
source
void
aboot_init(
const
struct
app_descriptor
{
unsigned
/*
#if
msm_wdog_init();
#endif
/*
if
(target_is_emmc_boot())
{
page_size
page_mask
mmc_blocksize
mmc_blocksize_mask
}
else
{
page_size
page_mask
}
ASSERT((MEMBASE
read_device_info(&device);
read_allow_oem_unlock(&device);
/*
#if
#if
if
(!check_alarm_boot())
#endif
dprintf(SPEW,
"Display
);
#if
/*
while
(pm_app_display_shutdown_in_prgs());
if
(!pm_appsbl_display_init_done())
target_display_init(device.display_panel);
else
display_image_on_screen();
#else
target_display_init(device.display_panel);
#endif
dprintf(SPEW,
"Display
);
#if
}
#endif
#endif
target_serialno((unsigned
char
*)
dprintf(SPEW,
"serial
,sn_buf);
sprintf
(secureboot_buf,
"%s"
,
"1"
:
"0"
);
memset
(display_panel_buf,
'\0'
,
/*
*
*
*/
if
(is_user_force_reset())
goto
normal_boot;
dprintf(CRITICAL,
"fastboot:
,device.is_unlocked);
/*
if
(keys_get_state(KEY_VOLUMEUP)
{
/***************add
if
(device.is_unlocked
dprintf(CRITICAL,
"fastboot:
);
goto
normal_boot;
}
else
{
/***************add
dprintf(ALWAYS,
"dload
);
reboot_device(EMERGENCY_DLOAD);
dprintf(CRITICAL,
"Failed
);
boot_into_fastboot
true
;
}
}
if
(!boot_into_fastboot)
{
if
(target_build_variant_user())
{
if
(keys_get_state(KEY_HOME)
boot_into_recovery
if
(!boot_into_recovery
(keys_get_state(KEY_BACK)
boot_into_fastboot
true
;
}
else
{
if
(keys_get_state(KEY_VOLUMEUP))
{
snprintf((
char
*)boot_reason_buf,
"%s"
,
"boot_with_factory"
);
dprintf(SPEW,
"boot_reason_buf:
,
}
else
if
(keys_get_state(KEY_VOLUMEDOWN))
boot_into_fastboot
true
;
}
}
#if
if
(fastboot_trigger())
boot_into_fastboot
true
;
#endif
#if
reboot_mode
#else
reboot_mode
#endif
if
(reboot_mode
{
boot_into_recovery
}
else
if
(reboot_mode
{
boot_into_fastboot
true
;
}
else
if
(reboot_mode
{
boot_reason_alarm
true
;
}
#if
#if
else
if
(reboot_mode
{
device.verity_mode
write_device_info(&device);
}
else
if
(reboot_mode
{
device.verity_mode
write_device_info(&device);
}
else
if
(reboot_mode
{
if
(send_delete_keys_to_tz())
ASSERT(0);
}
#endif
#endif
normal_boot:
if
(!boot_into_fastboot)
{
if
(target_is_emmc_boot())
{
if
(emmc_recovery_init())
dprintf(ALWAYS,
"error
);
if
(target_use_signed_kernel())
{
if
((device.is_unlocked)
{
#ifdef
set_tamper_fuse_cmd();
#endif
#if
set_tamper_flag(device.is_tampered);
#endif
}
}
boot_linux_from_mmc();
}
else
{
recovery_init();
#if
if
((device.is_unlocked)
set_tamper_flag(device.is_tampered);
#endif
boot_linux_from_flash();
}
dprintf(CRITICAL,
"ERROR:
"to
);
}
/*
/*
aboot_fastboot_register_commands();
/*
partition_dump();
/*
fastboot_init(target_get_scratch_address(),
//#if
//display_fastboot_menu();
display_fastboot();
//#endif
}
|
在aboot_init中完成的操作主要有:
2.1 根据target_is_emmc_boot()判断是否是从emmc存储设备上启动,然后分别获取对应存储设备的页大小和页掩码
2.2 取得设备的device_info信息,保存到device变量中。device_info结构体定义为:
source
#if
struct
device_info
{
unsigned
char
magic[DEVICE_MAGIC_SIZE];
bool
is_unlocked;
bool
is_tampered;
bool
is_verified;
bool
charger_screen_enabled;
char
display_panel[MAX_PANEL_ID_LEN];
char
bootloader_version[MAX_VERSION_LEN];
char
radio_version[MAX_VERSION_LEN];
char
sig[SIG_SIZE];
};
#else
struct
device_info
{
unsigned
char
magic[DEVICE_MAGIC_SIZE];
bool
is_unlocked;
bool
is_tampered;
bool
is_unlock_critical;
bool
charger_screen_enabled;
char
display_panel[MAX_PANEL_ID_LEN];
char
bootloader_version[MAX_VERSION_LEN];
char
radio_version[MAX_VERSION_LEN];
bool
verity_mode;
//
char
sig[SIG_SIZE];
};
#endif
|
这个结构体中包含的信息有:是否禁用fastboot,是否验证boot.img等。
2.3 调用target_display_init初始化lcd驱动,显示手机开机后的第一副图片。splash一般代表在系统启动阶段显示器用图形而非文本现实。
2.4 调用target_serialno获取emmc或者flash芯片的产品序列号,最后在启动kernel时通过cmdline中的androidboot.serialno参数传给内核。
2.5 调用memset清除屏幕
2.6 判断关机原因,如果是用户强制重启手机,这时在重启后跳转到normal_boot继续执行
2.7 判断音量上下键是否同时按下,之后如果进入下载模式失败,就设置fastboot模式标志boot_into_fastboot = true。
2.8 如果不是通过usb+上下键进入下载模式,判断如果home键和音量上键同时按下,设置recovery模式标志boot_into_recovery = 1, 如果back键和音量下键同时按下,设置fastboot模式标志。
2.9 如果没有按键驱动,设置boot_into_fastboot为true
2.10 根据硬件是否存在pon寄存器,调用check_hard_reboot_mode或者check_reboot_mode获取重启模式reboot_mode,设置相应的开机模式标志。以前使用的是共享内存记录重启模式,最新转为使用pmic的pon寄存器记录重启模式。
如果reboot_mode为RECOVERY_MODE,boot_into_recovery = 1
如果reboot_mode为FASTBOOT_MODE,boot_into_fastboot = 1
如果reboot_mode为ALARM_BOOT,boot_reason_alarm = true,这种情况时如果开启了闹钟并且设置了关机后闹钟有效,在关机后闹钟仍然会启动手机
2.11 在normal_boot:下,首先判断如果不是fastboot模式,然后通过target_is_emmc_boot判断是否从emmc或者ufs启动,
如果从emmc或者ufs启动,先执行emmc_recovery_init,emmc_recovery_init的实现主要在函数_emmc_recovery_init中,_emmc_recovery_init的定义在bootable/bootloader/lk//app/aboot/recovery.c中,主要工作为读取misc分区中的bcb,判断bcb的command域是否为boot-recovery,如果是update-radio还会检查radio的更新状态。 回到aboot_init中用target_use_signed_kernel判断是否使用了签名的kernel,最后执行boot_linux_from_mmc, 在boot_linux_from_mmc中将从emmc/ufs加载boot.img,选择dts,设置cmdline,跳转到kernel。
如果从nand flash启动,调用boot_linux_from_flash去启动内核。
2.12 之后的代码是fasboot模式或者正常启动失败的情况下才会执行到的,首先调用aboot_fastboot_register_commands注册fastboot支持的命令,在partition_dump中打印分区表信息, 执行fastboot_init初始化并启动fastboot,在display_fastboot_menu_thread中为fastboot提供了一个简易图形显示。