赞
踩
Android启动流程分析之一:Bootloader(基于高通芯片)
http://blog.csdn.net/ly890700/article/details/54586448
http://blog.csdn.net/kv110/article/details/39274737
Android系统启动过程由以下几个大步骤组成:
BootRom, Bootloader, Linux Kernel, android启动
BootRom没有源代码
Bootloader代码主要在/bootable下,包括对normal startup,及recovery mode, fastboot mode的处理
Linux主要是linux kernel启动
Android从porcess init (process 1)开始,带动其他process/service启动
下面主要讲Bootloader及linux调用过程
1.Bootloader调用过程
kernel/arch/arm/crt0.S _start call kmain() in bootable/bootloader/lk/kernel/main.c
kmain() 依次调用
thread_init_early()
arch_early_init()
platform_early_init()
target_early_init()
bs_set_timestamp()
call_constructors()
heap_init()
thread_init()
dpc_init()
timer_init()
create thread bootstrap2
其中 target_early_init()定义在 bootable/bootloader/lk/target/msm8226/init.c,会对uart初始化 uart_dm_init(1, 0, BLSP1_UART2_BASE);
bootstrap2() 又会调用
arch_init()
platform_init()
target_init()
apps_init()
target_init()定义在具体平台下,通常初始化些用到总线,keyboard, display等
apps_init() 定义在 bootable/bootloader/lk/app/app.c,会调用bootable/bootloader/lk/app/aboot/aboot.c里的 aboot_init()
aboot_init() 首先判断按键是recovery mode还是fastboot mode.
如果是fastboot mode,则调用fastboot_init(),该函数定义在bootable/bootloader/lk/app/aboot/fastboot.c。
如果是正常启动模式或recovery模式,则调用
emmc_recovery_init())
boot_linux_from_mmc()
boot_linux_from_mmc()定义在recovery/normal mode bootable/bootloader/lk/app/aboot/aboot.c
首先根据不同模式读出/boot, /recovery分区索引及偏移,接着会调用boot_linux()进入linux kernel.
2. Kernel启动调用过程
进入linux kernel首先是解压缩zimage,初始化MMU等硬件,进入到start_kernel()
是由kernel/arch/arm/kernel/head-common.S里的__INIT调用过来
start_kernel()定义在 kernel/init/main.c,会调用
boot_cpu_init();
page_address_init();
mm_init_owner(&init_mm, &init_task);
mm_init_cpumask(&init_mm);
setup_command_line(command_line);
setup_nr_cpu_ids();
setup_per_cpu_areas();
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
setup_log_buf(0);
vfs_caches_init_early();
mm_init();
sched_init();
…….
rest_init();
rest_init() create two threads,
在第一个线程执行函数kernel_init(),
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";
接着调用init_post(),
run_init_process(ramdisk_execute_command);
->kernel_execve
-> do_execve
process init就这样被创建了
3. 调试
Bootloader调试:
串口,T32, 写到文件里,打印到屏幕,设置硬件(GPIO, LED,vibrate)
Kernel: printk, cat proc/kmsg, dmesg
Init process: ERROR or change LOG_DEFAULT_LEVEL to 6
---------------------------------------------------------------------------------
msm8909+android5.1启动流程(2)---lk源代码目录及入口
1. Lk概述
LK是(L)ittle(K)ernel的缩写。
高通平台android普遍采用LK作为其bootloader,LK是一个开源项目。但是,LK只是整个系统的引导部分,所以它不是独立存在。LK是一个功能及其强大的bootloader,但现在只支持arm和x86平台。
LK的一个显著的特点就是它实现了一个简单的线程机制(thread),和对高通处理器的深度定制和使用。
2. 源代码目录
app //主函数启动app执行的目录,第一个app在app/aboot/aboot.c中
arch //体系代码包含x86和arm
dev //设备目录,包含显示器,键盘,net,usb等设备的初始化代码
include //头文件
kernel //kernel/main.c主函数以及kernel/thread.c线程函数
lib //库文件
make //编译规则
platform //不同平台代码mdmxxx,msmxxx,apqxxx,qsdxxx,还有共享的目录msm_shared
project //整个工程的编译规则
target //通用init.c,具体目标板的初始化(主要为板子设备资源init.c代码中),编译规则代码(一级s810.mk二级hdc8094.mk)
3. Lk入口
bootable\bootloader\lk\arch\arm\rule.mk文件下相关部分:
# potentially generated files that shouldbe cleaned out with clean make rule
GENERATED += \
$(BUILDDIR)/system-onesegment.ld\
$(BUILDDIR)/system-twosegment.ld
# rules for generating the linker scripts
…
$(BUILDDIR)/system-onesegment.ld:$(LOCAL_DIR)/system-onesegment.ld
@echogenerating $@
@$(MKDIR)
$(NOECHO)sed"s/%MEMBASE%/$(MEMBASE)/;s/%MEMSIZE%/$(MEMSIZE)/" < $< > $@
$(BUILDDIR)/system-twosegment.ld:$(LOCAL_DIR)/system-twosegment.ld
@echogenerating $@
@$(MKDIR)
$(NOECHO)sed"s/%ROMBASE%/$(ROMBASE)/;s/%MEMBASE%/$(MEMBASE)/;s/%MEMSIZE%/$(MEMSIZE)/"< $< > $@
接着看\bootable\bootloader\lk\arch\arm\system-onesegment.ld文件
OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
system-onesegment.ld连接文件中 ENTRY(_start0指定 LK 从_start 函数开始,_start 在 lk/arch/arm/crt0.S中 。crt0.S 主要做一些基本的 CPU 的初始化再通过 bl kmain ;跳转到 C 代码中。
kmain 在lk/kernel/main.c 中
MSM8909+Android5.1.1启动流程(3)---kmain()
1. thread_init_early()
// get us into some sort of thread context
thread_init_early();
让我们进入某种线程上下文,初始化线程系统,此函数只被调用一次。主要的工作如下:
(1) 初始化运行队列list_initialize(&run_queue[i])
(2) 初始化线程列表list_initialize(&thread_list)
(3) 创建一个线程来覆盖当前的运行状态,并初始化次线程的结构体。
/* create a thread to cover the currentrunning state */
thread_t*t = &bootstrap_thread;
init_thread_struct(t,"bootstrap");
/* half construct this thread, since we'realready running */
t->priority= HIGHEST_PRIORITY;
t->state= THREAD_RUNNING;
t->saved_critical_section_count= 1;
list_add_head(&thread_list,&t->thread_list_node);
current_thread= t;
2. arch_early_init()
做一些关闭cache,使能MMU的arm相关工作。
void arch_early_init(void)
{
/*turn off the cache */
arch_disable_cache(UCACHE);
/*set the vector base to our exception vectors so we dont need to double map at 0*/
#if ARM_WITH_MMU
arm_mmu_init();
#endif
/*turn the cache back on */
arch_enable_cache(UCACHE);
#if ARM_WITH_NEON
/*enable cp10 and cp11 */
uint32_tval;
__asm__volatile("mrc p15, 0, %0, c1, c0,2" : "=r" (val));
val|= (3<<22)|(3<<20);
__asm__volatile("mcr p15, 0, %0, c1, c0,2" :: "r" (val));
/*set enable bit in fpexc */
__asm__volatile("mrc p10, 7, %0, c8, c0,0" : "=r" (val));
val|= (1<<30);
__asm__volatile("mcr p10, 7, %0, c8, c0,0" :: "r" (val));
#endif
}
3. platform_early_init()
平台的早期初始化
void platform_early_init(void)
{
board_init();
platform_clock_init();
qgic_init();
qtimer_init();
}
3.1 board_init()
void board_init()
{
platform_detect();
target_detect(&board);//对于msm8909是空函数,在platform_detect()实现
target_baseband_detect(&board);//填充board->baseband = BASEBAND_MSM;
}
platform_detect()
Smem:共享内存
struct smem {
structsmem_proc_comm proc_comm[4];
unsignedversion_info[32];
structsmem_heap_info heap_info;
structsmem_alloc_info alloc_info[SMEM_MAX_SIZE];
};
共享内存对应的结构体
SMEM :sharedmemory,是高通平台各子系统共享信息的一种机制,通过SMEM机制,PBL可以将信息传递给SBL1,SBL1可以将信息传递给RPM、LK。下面分析一个SMEM信息传递的具体实现过程。
Platform id信息
SBL1会将board levelplatform id信息通过SMEM机制保存,LK在启动过程中会自动platform detect,检测当前平台board infor信息,然后填充board结构体实例的值
struct board_data {
uint32_tplatform;
uint32_tfoundry_id;
uint32_tplatform_version;
uint32_tplatform_hw;
uint32_tplatform_subtype;
uint32_ttarget;
uint32_tbaseband;
structboard_pmic_data pmic_info[MAX_PMIC_DEVICES];
uint32_tplatform_hlos_subtype;
};
根据检测到的board infor加载相应的device tree。
3.2 platform_clock_init()
调用clk_init(msm_clocks_msm8909,ARRAY_SIZE(msm_clocks_msm8909))初始化时钟查询表
/* Clock lookup table */
static struct clk_lookup msm_clocks_msm8909[]=
{
CLK_LOOKUP("sdc1_iface_clk",gcc_sdcc1_ahb_clk.c),
CLK_LOOKUP("sdc1_core_clk", gcc_sdcc1_apps_clk.c),
CLK_LOOKUP("sdc2_iface_clk",gcc_sdcc2_ahb_clk.c),
CLK_LOOKUP("sdc2_core_clk", gcc_sdcc2_apps_clk.c),
CLK_LOOKUP("uart1_iface_clk",gcc_blsp1_ahb_clk.c),
CLK_LOOKUP("uart1_core_clk", gcc_blsp1_uart1_apps_clk.c),
…
}
3.3 qgic_init()
QGIC:Qualcomm GenericInterrupt Controller高通通用中断控制器
初始化QGIC,包括初始化QGIC的分配器(distributor)和初始化CPU具体的控制器。
3.4 qtimer_init()
获取定时器的频率
4. target_early_init()
这里就是串口初始化uart_dm_init(1, 0,BLSP1_UART0_BASE)---> uart_dm_init()
从时钟查找表msm_clocks_msm8909[]串口并配置其时钟频率、配置GPIO为串口、配置串口寄存器等。
5. bs_set_timestamp(BS_BL_START)
设置时间戳,这里的bs指boot stats,引导统计。
6. call_constructors()
构造函数相关初始化
bootloader的构造器一般完成的是初始化设备对象的操作,由于不同的系统在bootloader阶段需要初始化的设备不同,所以需要用户自己调用这些初始化函数。
根据函数名称,就是字面意思——调用构造器,构造器是定义在__ctor_list和__ctor_end之间的函数指针。至于__ctor_list和__ctor_end之间有哪些函数你可以看编译之后输出的map文件,或者链接脚本里面可能也有定义。
7. heap_init()
启动内核堆。
8. __stack_chk_guard_setup()
负责生成canary值
http://toutiao.com/a6244754891979129089/
9. thread_init()
初始化线程系统。
10. dpc_init()
dpc:DelayedProcedure Call 延迟过程调用的缩写,初始化dpc系统,相关事件初始化、创建名字为dpc的线程并调用。
11. timer_init()
初始化lk定时器。
12. bootstrap2
thread_resume(thread_create("bootstrap2",&bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
// enable interrupts
exit_critical_section();
// become the idle thread
thread_become_idle();
创建名字为bootstrap2的线程并调用,调用thread_become_idle()让当前线程处于idle状态。
后面接着学习线程bootstrap2。
MSM8909+Android5.1.1启动流程(5)---app_init()
调用关系
kmain()--->bootstrap2()--->apps_init()
- /* one time setup */
- void apps_init(void)
- {
- conststruct app_descriptor *app;
-
- /*call all the init routines */
- 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段里的函数,并调用LK中所谓的app的init函数,然后调用start_app(app),哪些app被放入 boot thread section, 则定义在 include/app.h 中的 APP_START(appname),app.h文件中相关定义:
#define APP_START(appname) structapp_descriptor _app_##appname __SECTION(".apps") = { .name =#appname,
#define APP_END };
aboot_init 就将在这里开始被运行,androidlinux 内核的加载工作就在 aboot_init 中完成的 。aboot.c中的定义如下:
APP_START(aboot)
.init= aboot_init,
APP_END
为了更好理解,看对应的system-onesegment.ld文件(该文件在”bootable\bootloader\lk\ build-目标平台”目录下),
system-onesegment.ld
.rodata : {
*(.rodata.rodata.* .gnu.linkonce.r.*)
.= ALIGN(4);
__commands_start= .;
KEEP(*(.commands))
__commands_end= .;
.= ALIGN(4);
__apps_start= .;
KEEP(*(.apps))
__apps_end= .;
.= ALIGN(4);
__rodata_end= . ;
}
原来,在其最终的连接文件里,是将需要启动的apps括在了SECTIONS下的.rodata段中,且以__apps_start为开头,以__apps_end标志结束(这里涉及到文件结构的部分内容,内容拓展可以看《程序员的自我修养—链接、装载与库》一书)。
正如网上所说“在 app 中只要像 app/aboot/aboot.c 指定就会在 bootloader bootup 时放入 thread section 中被执行”。这点我们可以直接在整个lk中搜索关键字“APP_START”会发现我们的bootloader中到底有多少个类似这样的app(不同的bootloader情况有所不同):
图1
可知满足条件的app有pcitests、stringtests、tests、aboot、clocktests和shell
后面我们接着学习aboot_init()
参考:
平述factory reset ——从main system到重引导流程
http://www.voidcn.com/blog/LoongEmbedded/article/p-6011547.html
------------------------------------------------
系统启动流程分析图
如果以上条件皆不满足,则进入正常启动序列,系统会加载boot.img文件,然后加载kernel,在内核加载完成之后,会根据内核的传递参数寻找android的第一个用户进程,即init进程,该进程根据init.rc以及init.$(hardware).rc脚本文件来启动android的必要的服务,直到完成android系统的启动。
当进入recovery模式时,系统加载的是recovery.img文件,该文件内容与boot.img类似,也包含了标准的内核和根文件系统。但是recovery.img为了具有恢复系统的能力,比普通的boot.img目录结构中:
1、多了/res/images目录,在这个目录下的图片是恢复时我们看到的背景画面。
2、多了/sbin/recovery二进制程序,这个就是恢复用的程序。
3、/sbin/adbd不一样,recovery模式下的adbd不支持shell。
4、初始化程序(init)和初始化配置文件(init.rc)都不一样。这就是系统没有进入图形界面而进入了类似文本界面,并可以通过简单的组合键进行恢复的原因。
与正常启动系统类似,也是启动内核,然后启动文件系统。在进入文件系统后会执行/init,init的配置文件就是 /init.rc。这个配置文件位于bootable/recovery/etc/init.rc。查看这个文件我们可以看到它做的事情很简单:
1) 设置环境变量。
2) 建立etc连接。
3) 新建目录,备用。
4) 挂载文件系统。
5) 启动recovery(/sbin/recovery)服务。
6) 启动adbd服务(用于调试)。
上文所提到的fastboot模式,即命令或SD卡烧写模式,不加载内核及文件系统,此处可以进行工厂模式的烧写。
综上所述,有三种进入recovery模式的方法,分别是开机时按组合键,写SMEM中的reboot_mode变量值,以及写位于MISC分区的BCB中的command字段。
除了上述方式还可以通过命令进入recovery模式:adb reboot recovery
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。