高通平台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\n\n");  //開始進入LK,INFO級別在console打印
24 
25     // initialize the threading system
26     dprintf(SPEW, "initializing threads\n");    //SPEW級別在console口不打印
27     thread_init();
28 
29     // initialize the dpc system
30     dprintf(SPEW, "initializing dpc\n");
31     dpc_init();
32 
33     // initialize kernel timers
34     dprintf(SPEW, "initializing timers\n");
35     timer_init();
36 
37  // create a thread to complete system initialization    -->創建線程完成系統初始化,跳轉到第二階段
38     dprintf(SPEW, "creating bootstrap completion thread\n");
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()\n");
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()\n");//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\n");
32     target_display_init(device.display_panel);
33     dprintf(SPEW, "Display Init: Done\n");
34 #endif
35 
36 
37     target_serialno((unsigned char *) sn_buf);   //獲取串口號
38     dprintf(SPEW,"serial number: %s\n",sn_buf);
39 
40       memset(display_panel_buf, '\0', 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\n");
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\n",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!\n");
21                 }
22             }
23 #endif
24 
25 }

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM