本文轉載自: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 }