pre loader简单分析

ZZ from: http://blog.csdn.net/ly601579033/article/details/48318239

=============================================================

大致的流程图如下:

pre loader简单分析_第1张图片

上电后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执行<代码都在lk目录下面>

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;

pre loader简单分析_第2张图片

进入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进程)

到此,完成初步的总结。

你可能感兴趣的:(pre loader简单分析)