高通平台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 }