当前位置:   article > 正文

Android 系统启动过程_[android]高通平台bootloader启动流程

[android]高通平台bootloader启动流程

 

 

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()

 

  1. /* one time setup */
  2. void apps_init(void)
  3. {
  4. conststruct app_descriptor *app;
  5. /*call all the init routines */
  6. for(app = &__apps_start; app != &__apps_end; app++) {
  7. if(app->init)
  8. app->init(app);
  9. }
  10. /*start any that want to start on boot */
  11. for(app = &__apps_start; app != &__apps_end; app++) {
  12. if(app->entry && (app->flags & APP_FLAG_DONT_START_ON_BOOT) ==0) {
  13. start_app(app);
  14. }
  15. }
  16. }

 

 

 

遍历所有在__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

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/296538
推荐阅读
相关标签
  

闽ICP备14008679号