ZZ from: http://blog.csdn.net/ly601579033/article/details/48318239
=============================================================
大致的流程图如下:
上电后RESET,ARM核开始执行Boot ROM,具体操作有:(代码固化在ROM中)
1、 初始化内置SRAM堆栈
2、 初始化nand/emmc(手机内置存储)
3、 把nand内存储的Pre-loader导入SRAM中
4、 跳到SRAM中执行Pre-loader
在内置SRAM中执行Pre-loader操作:(到bootloader/preloader下进行操作)<代码都在preloader目录下面>
1、配置c运行环境(寄存器、堆栈、BSS等) BSS静态内存段(未初始化)、数据段(初始化)、堆段、栈段
2、初始化外部DRAM的timer、时钟、UART、EMI(DRAM防静电干扰)
3、跳到DRAM执行LK(little kernel)
具体源码如下:
先执行一个ld链接脚本——显示一部分(在bootable/bootloader/preloader平台的link_descriptor.ld文件)
OUTPUT_ARCH(arm)
ENTRY(_start) //进入_start执行
romBase = 0x00201000;
ramBase = 0x00102180;
MEMORY {
ram : ORIGIN= ramBase, LENGTH = 0xBA80
rom : ORIGIN= romBase, LENGTH = 0x1F000
}
_start在init.s中位置,在ld里面定义c运行环境 (c运行环境(寄存器、堆栈、BSS等)地址)在这里被初始化
.globl _start
_start:
b resethandler
… … …
resethandler: //进行reset操作,并disable irq
LDR r6,=bldr_args_addr
STR r4, [r6]
MOV r0, #0
… … … …
LDR r0, =bldr_args_addr //跳转操作,bldr_args_addr地址在之前有定义
B main
进入main.c文件执行main操作
void main(u32 *arg){
mtk_uart_init(UART_SRC_CLK_FRQ, CFG_LOG_BAUDRATE);
bldr_pre_process(); //外部RWAM的timer、时钟、UART设置
bldr_handshake(&handler); //UART、USB握手测试(保证可以通信)
bldr_load_images; //导入Uboot镜像(镜像地址在此函数内的addr有定义)
bldr_post_process();
bldr_jump(jump_addr,jump_arg, sizeof(boot_arg_t)); //跳转到DRAM执行
}
在跳转到DRAM执行LK时,传递了参数哪些参数呢?
bldr_jump(jump_addr, jump_arg, sizeof(boot_arg_t));
1、jump_addr跳转到LK执行的地址
2、向LK传递参数的地址
3、传递参数的大小
传递了一个boot_arg_t 结构的数据,这个结构数据定义在platform.c的platform_set_boot_args()函数下定义
在LK里面进行外设的初始化,加载内核并启动android系统初始化等操作,具体如下:
1、获取Pre-loader传递的参数
2、使能MMU单元
3、使能外设
4、设置Boot模式(这里可以进入fastboot模式)
5、导入kernel
6、调到kernel进行加载
跳转到LK中,在lk\arch\arm\crt0.s中
.globl _start
_start:
b reset
… … …
reset:
ldr r6, =BOOT_ARGUMENT_LOCATION //把寄存器数据转移r6中
str r4, [r6]
在这个文件执行后,会跳转到kmain执行(通过 bl kmain)
kmain 在 lk/kernel/main.c 中
void kmain(void){
//初始化线程队列,创建一个bootstrap2线程并加入到队列中,这里可以看到线程有6种状态
thread_init_early();
六种状态定义如下:
enum thread_state {
THREAD_SUSPENDED= 0, //中止
THREAD_READY, //准备
THREAD_RUNNING, //运行
THREAD_BLOCKED, //阻塞
THREAD_SLEEPING, //休眠
THREAD_DEATH, //死亡
};
// MMU初始化、相关寄存器初始化
arch_early_init();
//中断初始化、timer初始化准备、GPIO初始化、uart初始化、WDT(看门狗)初始化、SRAM修复、I2C固件初始化、Led初始化(背光)、pmic初始化(电源)
platform_early_init();
//null操作,在此没意义
target_early_init();
dprintf(INFO,"welcome to lk\n\n");
//构造器,循环执行定义在.ctros里面的func
dprintf(SPEW,"calling constructors\n");
call_constructors();
// thekernel heap初始化
dprintf(SPEW,"initializing heap\n");
heap_init();
//initialize the threading system
dprintf(SPEW,"initializing threads\n");
thread_init();
//软中断系统
dprintf(SPEW,"initializing dpc\n");
dpc_init();
// timer初始化
dprintf(SPEW,"initializing timers\n");
timer_init();
#ifdef MTK_LK_IRRX_SUPPORT
mtk_ir_init(0);
#endif
#if (!ENABLE_NANDWRITE)
// resume线程bootstrap2
dprintf(SPEW,"creating bootstrap completion thread\n");
thread_resume(thread_create(“bootstrap2”,&bootstrap2, NULL,DEFAULT_PRIORITY,DEFAULT_STACK_SIZE));
// enableinterrupts
exit_critical_section();
// 把当前线程变为idle线程
thread_become_idle();
#endif
}
到此,当前线程变为idle,开启了新的线程bootstrap2。线程优先级为DEFAULT_PRIORITY,列出线程优先级的定义:
/* thread priority */
#define NUM_PRIORITIES 32
#define LOWEST_PRIORITY 0 //最低优先级0
#define HIGHEST_PRIORITY (NUM_PRIORITIES - 1) //最高优先级31
#define DPC_PRIORITY HIGHEST_PRIORITY //最高优先级31
#define IDLE_PRIORITY LOWEST_PRIORITY //空闲 最低优先级0
#define LOW_PRIORITY (NUM_PRIORITIES / 4) //低优先级8
#define DEFAULT_PRIORITY (NUM_PRIORITIES / 2) //默认优先级16
#define HIGH_PRIORITY ((NUM_PRIORITIES / 4) * 3) //高优先级24
进入bootstrap2线程:
static int bootstrap2( void *arg){
// platform初始化(nand初始化、环境变量env获取并打印、LCM显示相关(显示LOGO)、选择进入模式<这里按键判断可以进入boot烧写模式>)
dprintf(SPEW,"initializing platform\n");
platform_init();
// null函数
dprintf(SPEW,"initializing target\n");
target_init();
//执行__apps_start到__apps_end的app
dprintf(SPEW,"calling apps_init()\n");
apps_init();
return 0;
}
apps跳到bootable\bootloader\lk\app\mt_boot\mt_boot.c执行mt_boot_init
看代码:
APP_START(mt_boot)
.init = mt_boot_init, //执行这个操作
APP_END
进入mt_boot_init之后会执行boot_linux_from_storage操作,然后会执行
boot_linux((void *)CFG_BOOTIMG_LOAD_ADDR, (unsigned*)CFG_BOOTARGS_ADDR,
(char*)commanline, board_machtype(), (void *)CFG_RAMDISK_LOAD_ADDR, g_rimg_sz);
CFG_BOOTIMG_LOAD_ADDR ————DRAM中BOOTIMG的物理地址
CFG_BOOTARGS_ADDR ————向DRAM传递参数的位置
Commanline ————传递的参数commanline
board_machtype() ————board信息
CFG_RAMDISK_LOAD_ADDR ————ramdisk地址
注意:
1、传递到kernel的数据必须以ATAG的结构传递(便于接收)
2、关闭cache与MMU
最后执行————entry(0, machtype, tags);跳转到kernel执行 CFG_BOOTIMG_LOAD_ADDR地址处
LK向kernel传递的参数:(数据被封装的过程)
ATAG的结构封装必须以CORE开始,以END结束
/* CORE*/
*ptr++ = 2;
*ptr++ =0x54410001;
ptr =target_atag_boot(ptr);
ptr =target_atag_mem(ptr);
ptr =target_atag_meta(ptr);
ptr =target_atag_commmandline(ptr, cmdline);
ptr =target_atag_initrd(ptr,(unsigned long) ramdisk, ramdisk_size);
ptr =target_atag_videolfb(ptr);
/* END */
*ptr++ = 0;
*ptr++ = 0;
分析源码 ::
unsigned*target_atag_boot(unsigned *ptr){
*ptr++ =tag_size(tag_boot); //tag_size大小
*ptr++ =ATAG_BOOT; //tag name
*ptr++ =g_boot_mode; //传递的数据
return ptr;
}
unsigned *target_atag_mem(unsigned *ptr)
{
int i;
for (i = 0; i< g_nr_bank; i++) {
*ptr++ =4; //tag size
*ptr++ =ATAG_MEM; //tag name
*ptr++ =bi_dram[i].size;
*ptr++ =bi_dram[i].start;
}
关于ATAG数据的定义以及传递到SRAM后的位置图
typedef enum{
NORMAL_BOOT =0,
META_BOOT =1,
RECOVERY_BOOT= 2,
SW_REBOOT =3,
FACTORY_BOOT= 4,
ADVMETA_BOOT= 5,
ATE_FACTORY_BOOT = 6,
ALARM_BOOT =7,
#if defined (MTK_KERNEL_POWER_OFF_CHARGING)
KERNEL_POWER_OFF_CHARGING_BOOT = 8,
LOW_POWER_OFF_CHARGING_BOOT = 9,
#endif
FASTBOOT =99,
DOWNLOAD_BOOT= 100,
UNKNOWN_BOOT
} BOOTMODE;
进入kernel代码执行:<代码都在kernel中执行>
1、 指针入口操作
2、 Start Kernel
3、 Boot 参数
4、 Kthread
5、 Kernel_init()
6、 Init.rc
指针入口做的操作:
1、 超级用户模式,disable irq
2、 查询处理器/机器type
3、 检测ATAG格式
4、 建立页表,启动MMU
5、 调到init/main.c的start_kernel执行
asmlinkage void __init start_kernel(void){
//mem初始化、cache初始化(装载页表)
mm_init();
//主要是解析传递过来的参数tags
setup_arch(&command_line);
/*
* Set up the scheduler prior starting anyinterrupts (such as the
* timer interrupt). Full topology setuphappens at smp_init()
* time - but meanwhile we still have afunctioning scheduler.
*/
sched_init();
init_IRQ();
/*
* HACK ALERT! This is early. We're enablingthe console before
* we've done PCI setups etc, andconsole_init() must be aware of
* this. But we do want output early, in casesomething goes wrong.
*/
console_init();
//kernel初始化、开启Kthread
rest_init();
}
在rest_init()执行驱动加载等操作---- >
static noinline void __init_refok rest_init(void){
rcu_scheduler_starting();
/*
* We need to spawn init first so that itobtains pid 1, however
* the init task will end up wanting to createkthreads, which, if
* we schedule it before we create kthreadd,will OOPS.
*/
kernel_thread(kernel_init,NULL, CLONE_FS | CLONE_SIGHAND);
pid =kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
rcu_read_lock();
kthreadd_task= find_task_by_pid_ns(pid, &init_pid_ns);
rcu_read_unlock();
complete(&kthreadd_done);
/* Callinto cpu_idle with preempt disabled */
cpu_startup_entry(CPUHP_ONLINE);
}
Kernel_init做了哪些操作————加载Root文件系统、初始化驱动模块、run init进程
run_init_process("/sbin/init") 、run_init_process("/etc/init") 、run_init_process("/bin/init")、run_init_process("/bin/sh")
开启init进程————设备初始化、开启Servicemanamger和zygote(两个android进程)
到此,完成初步的总结。