高通平台Bootloader启动流程【转】

本文转载自:http://blog.csdn.net/fang_first/article/details/49615631

====================基本知识=======================
LK是(L)ittle (K)ernel的缩写。
高通平台android普遍采用LK作为其bootloader,LK是一个开源项目。但是,LK只是整个系统的引导部分,所以它不是独立存在。LK是一个功能及其强大的bootloader,但现在只支持arm和x86平台。
LK的一个显著的特点就是它实现了一个简单的线程机制(thread),和对高通处理器的深度定制和使用。
 
====================源码架构=======================

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)
 
====================程序执行流程============================
主函数lk/kernel/main.c
 1 /* called from crt0.S */
 2 void kmain(void) __NO_RETURN __EXTERNALLY_VISIBLE;
 3 void kmain(void)     //从kmain函数开始执行
 4 {
 5            thread_init_early();      //线程初始化
 6            arch_early_init();          //平台体系x86或者arm初始化,类似uboot的第一阶段汇编,在arch/arm下面实现
 7 实现功能:关闭cache,设置异常向量,mmu初始化,打开cache
 8            // do any super early platform initialization
 9            platform_early_init();---->                                    //开始涉及到具体平台
10 void platform_early_init(void)
11 {
12     board_init();                  //目标平台板的初始化
13     platform_clock_init();  //平台时钟初始化msm8994_clock
14     qgic_init();                  //通用IO通道初始化
15     qtimer_init();              //时钟初始化
16     scm_init();                   //单片机初始化
17 }
18                     
19           // do any super early target initialization
20            target_early_init();    //只初始化串口为了打印信息,与后面的target_init对应
21 以上采用层层递进的关系进行初始化
22 
23    dprintf(INFO, "welcome to lk

");  //开始进入LK,INFO级别在console打印
24 
25     // initialize the threading system
26     dprintf(SPEW, "initializing threads
");    //SPEW级别在console口不打印
27     thread_init();
28 
29     // initialize the dpc system
30     dprintf(SPEW, "initializing dpc
");
31     dpc_init();
32 
33     // initialize kernel timers
34     dprintf(SPEW, "initializing timers
");
35     timer_init();
36 
37  // create a thread to complete system initialization    -->创建线程完成系统初始化,跳转到第二阶段
38     dprintf(SPEW, "creating bootstrap completion thread
");
39            /*jump to bootstrap2*/
40           thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
41 
42     // become the idle thread    //变成空闲线程
43     thread_become_idle();
44 }
45 
46 static int bootstrap2(void *arg)
47 {
48             platform_init();              --->void platform_init(void)      //msm8994
49 {
50     dprintf(INFO, "platform_init()
");
51 #if ENABLE_XPU_VIOLATION
52     scm_xpu_err_fatal_init();
53 #endif
54 }
55            target_init();  //各种板子资源初始化,mmc,sdc,usb,volumn等---->
56               //里面最重要的是mmc的初始化   target_sdc_init();
57               //还有RPM                                  rpm_smd_init();
58 
59             dprintf(SPEW, "calling apps_init()
");//app初始化以及启动app
60             apps_init();//开始执行app/aboot.c中的aboot_init函数
61 }
接下来开始执行app/aboot/aboot.c
 1 在amboot.c的源码最底端:
 2 APP_START(aboot)     //可以看出上述的app启动的第一个就是aboot_init
 3     .init = aboot_init,
 4 APP_END
 5 
 6 /* each app needs to define one of these to define its startup conditions */每个app需要的定义
 7 struct app_descriptor {
 8     const char *name;
 9     app_init  init;
10     app_entry entry;
11     unsigned int flags;
12 };
13 开始研究aboot_init函数:
14 void aboot_init(const struct app_descriptor *app)
15 {
16           /* Setup page size information for nv storage */首先判断从哪启动emmc还是flash
17     if (target_is_emmc_boot())
18     {
19         page_size = mmc_page_size();
20         page_mask = page_size - 1;
21     }
22     else
23     {
24         page_size = flash_page_size();
25         page_mask = page_size - 1;
26     }
27         read_device_info(&device);      //读取设备信息
28 
29     /* Display splash screen if enabled */
30 #if DISPLAY_SPLASH_SCREEN                           //初始化显示屏
31     dprintf(SPEW, "Display Init: Start
");
32     target_display_init(device.display_panel);
33     dprintf(SPEW, "Display Init: Done
");
34 #endif
35 
36 
37     target_serialno((unsigned char *) sn_buf);   //获取串口号
38     dprintf(SPEW,"serial number: %s
",sn_buf);
39 
40       memset(display_panel_buf, '', MAX_PANEL_BUF_SIZE);
41     /*如果用户强制重启是进入正常模式的,不会进入fastboot模式,然而在实现中该函数返回0,不执行
42      * Check power off reason if user force reset,
43      * if yes phone will do normal boot.
44      */
45     if (is_user_force_reset())
46         goto normal_boot;
47 
48     接下来就做一些除了boot up之外的一些事情,这里面主要判断组合按键,其中可以进入dload(livesuit)模式和recovery模式
49      其中recovery模式进入Linux内核,启动recovery映像,通过界面选择烧写的软件包update.zip
50 注:android镜像烧写总共有三种:fastboot(调试用),livesuit(下载整个镜像),recovery(启动recovery镜像)
51 
52     然后判断是正常启动还是非正常启动,如果正常启动就recovery_init然后直接启动内核(包括传参)
53 两种情况:emmc和flash启动
54             if (target_is_emmc_boot())
55         {
56             if(emmc_recovery_init())
57                 dprintf(ALWAYS,"error in emmc_recovery_init
");
58             if(target_use_signed_kernel())
59             {
60                 if((device.is_unlocked) || (device.is_tampered))
61                 {
62                 #ifdef TZ_TAMPER_FUSE
63                     set_tamper_fuse_cmd();
64                 #endif
65                 #if USE_PCOM_SECBOOT
66                     set_tamper_flag(device.is_tampered);
67                 #endif
68                 }
69             }
70 
71             boot_linux_from_mmc();    ---->lk的启动画面也在里面,其实就是完成启动前的最后准备工作
72         }
73         else
74         {
75             recovery_init();
76     #if USE_PCOM_SECBOOT
77         if((device.is_unlocked) || (device.is_tampered))
78             set_tamper_flag(device.is_tampered);
79     #endif
80             boot_linux_from_flash();
81         }
82 
83     如果是非正常启动就进入fastboot模式,之前进行fastboot命令的注册以及启动fastboot
84     /* register aboot specific fastboot commands */注册fastboot命令
85     aboot_fastboot_register_commands();
86 
87     /* dump partition table for debug info */
88     partition_dump();
89 
90     /* initialize and start fastboot */初始化fastboot以及启动
91     fastboot_init(target_get_scratch_address(), target_get_max_flash_size());
92 }

现在分析fastboot_init函数做些什么工作:

 1 int fastboot_init(void *base, unsigned size)
 2 {
 3     /* target specific initialization before going into fastboot. */进入fastboot前的目标板初始化
 4     target_fastboot_init();
 5 
 6          /* setup serialno */创建串口号
 7     target_serialno((unsigned char *) sn_buf);
 8     dprintf(SPEW,"serial number: %s
",sn_buf);
 9     surf_udc_device.serialno = sn_buf;
10 
11         /* initialize udc functions to use dwc controller */初始化usb控制器,因为fastboot和板子通过usb进行通信
12          /* register udc device */注册usb controller设备
13 
14          /* register gadget */注册gadget
15 
16 thr = thread_create("fastboot", fastboot_handler, 0, DEFAULT_PRIORITY, 4096);//创建线程
17                           ---->static int fastboot_handler(void *arg)
18 {
19     for (;;) {
20         event_wait(&usb_online);//等待usb连接
21         fastboot_command_loop();//循环处理fastboot命令
22     }
23     return 0;
24 }
25 }
26 大多数fastboot命令cmd_xxx是在aboot.c中实现的,然后进行注册
现在分析 boot_linux_from_mmc函数做些什么工作:
 常用结构体:
 1 struct boot_img_hdr
 2 {
 3     unsigned char magic[BOOT_MAGIC_SIZE];
 4 
 5     unsigned kernel_size;  /* size in bytes */
 6     unsigned kernel_addr;  /* physical load addr */
 7 
 8     unsigned ramdisk_size; /* size in bytes */
 9     unsigned ramdisk_addr; /* physical load addr */
10 
11     unsigned second_size;  /* size in bytes */
12     unsigned second_addr;  /* physical load addr */
13 
14     unsigned tags_addr;    /* physical addr for kernel tags */
15     unsigned page_size;    /* flash page size we assume */
16     unsigned dt_size;      /* device_tree in bytes */
17     unsigned unused;    /* future expansion: should be 0 */
18 
19     unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */
20 
21     unsigned char cmdline[BOOT_ARGS_SIZE];    //串口的传参在这里
22 
23     unsigned id[8]; /* timestamp / checksum / sha1 / etc */
24 };

现在研究串口cmdline在哪打印的:

 1 void boot_linux(void *kernel, unsigned *tags,
 2         const char *cmdline, unsigned machtype,
 3         void *ramdisk, unsigned ramdisk_size)
 4 {
 5         final_cmdline = update_cmdline((const char*)cmdline);
 6 
 7 }
 8 
 9 void aboot_init(const struct app_descriptor *app)
10 {
11         bool boot_into_fastboot = false;    //判断是否进入fastboot模式
12 #if UART_INPUT_INTO_FASTBOOT
13     char getc_value;
14 #endif
15 
16 #if UART_INPUT_INTO_FASTBOOT    //经测验,按键f要一直按住,否则很难检测到,后续可以考虑延迟一段时间
17             if(dgetc(&getc_value, 0) >= 0) {
18                 if(getc_value == 'f') {
19                     boot_into_fastboot = true;
20                     dprintf(INFO,"keyboard is pressed, goto fastboot mode!
");
21                 }
22             }
23 #endif
24 
25 }
 
原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/6423492.html