zoukankan      html  css  js  c++  java
  • 高通平台的bootloader过程【转】

    ====================基本知识=======================
    LK是(L)ittle (K)ernel的缩写。
    高通平台android普遍采用LK作为其bootloader,LK是一个开源项目。但是,LK只是整个系统的引导部分,所以它不是独立存在。LK是一个功能及其强大的bootloader,但现在只支持arm和x86平台。
    LK的一个显著的特点就是它实现了一个简单的线程机制(thread),和对高通处理器的深度定制和使用。

    启动流程 
    1. 芯片上电复位到地址0, RPM PBL开始运行; 
    2. RPM PBL执行基本的电量和功率检测,然后复位APP处理器(地址0xFC010000); 
    3. APPS PBL在A53上执行,他从启动设备中加载(并鉴定)SBL1镜像到OCMEM; 
    4. APPS PBL将RVBAR设置为SBL1的入口,设置RMR_EL3为64位模式,然后触发热启动,程序转到SBL1; 
    5. SBL1开始运行,他首先初始化DDR,然后加载(并鉴定)HYP和TZ镜像; 
    6. SBL1将执行权转交给TZ; 
    7. TZ安全监管器建立安全环境; 
    8. TZ QSEE内核运行; 
    9. TZ应用程序(32/64位)运行,他们初始化系统; 
    10. TZ将执行权转交给HYP(监管程序); 
    11. 监管程序运行,他建立调试管理器,然后将执行权交回SBL1; 
    12. SBL1加载(并鉴定)RPM 固件,设置RPM固件准备就绪魔数; 
    13. RPM 固件在RPM处理器上运行; 
    14. SBL1加载(并鉴定)SDI(系统调试镜像),SDI pass 0开始执行; 
    15. SBL1加载(并鉴定)HLOS APPSBL(High-Level Operationg System,高级操作系统的SBL,指lk(little kernel)); 
    16. SBL1将执行权转给HLOS APPSBL; 
    17. HLOS APPSBL加载(并鉴定)HLOS kernel(实指linux kernel); 
    18. HLOS APPSBL通过TZ陷阱系统调用(TZ trap syscall)将执行权转交给HLOS kernel; 
    19. 根据需要,HLOS kernel从启动设备中加载MBA(Modem Boot Authenticator)到DDR; 
    20. HLOS kernel复位Modem处理器,Modem PBL开始运行; 
    21. (此条疑似错误,参考80-NM328-6第26页); 
    22. Modem PBL从DDR中将MBA拷贝到modem TCM中,然后在modem TCM中鉴定MBA; 
    23. 根据需要,HLOS kernel从启动设备中加载MPSS镜像到DDR中; 
    24. MBA鉴定DDR中的MPSS镜像; 
    25. 根据需要,HLOS使用PIL加载LPASS镜像; 
    26. 根据需要,HLOS使用PIL加载Venus镜像。

    ====================源码架构=======================

    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
    /* called from crt0.S */
    void kmain(void) __NO_RETURN __EXTERNALLY_VISIBLE;
    void kmain(void)     //从kmain函数开始执行
    {
               thread_init_early();      //线程初始化
               arch_early_init();          //平台体系x86或者arm初始化,类似uboot的第一阶段汇编,在arch/arm下面实现
    实现功能:关闭cache,设置异常向量,mmu初始化,打开cache
               // do any super early platform initialization
               platform_early_init();---->                                    //开始涉及到具体平台
    void platform_early_init(void)
    {
        board_init();                  //目标平台板的初始化
        platform_clock_init();  //平台时钟初始化msm8994_clock
        qgic_init();                  //通用IO通道初始化
        qtimer_init();              //时钟初始化
        scm_init();                   //单片机初始化
    }
                        
              // do any super early target initialization
               target_early_init();    //只初始化串口为了打印信息,与后面的target_init对应
    以上采用层层递进的关系进行初始化

       dprintf(INFO, "welcome to lk ");  //开始进入LK,INFO级别在console打印

        // initialize the threading system
        dprintf(SPEW, "initializing threads ");    //SPEW级别在console口不打印
        thread_init();

        // initialize the dpc system
        dprintf(SPEW, "initializing dpc ");
        dpc_init();

        // initialize kernel timers
        dprintf(SPEW, "initializing timers ");
        timer_init();

     // create a thread to complete system initialization    -->创建线程完成系统初始化,跳转到第二阶段
        dprintf(SPEW, "creating bootstrap completion thread ");
               /*jump to bootstrap2*/
              thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));

        // become the idle thread    //变成空闲线程
        thread_become_idle();
    }

    static int bootstrap2(void *arg)
    {
                platform_init();              --->void platform_init(void)      //msm8994
    {
        dprintf(INFO, "platform_init() ");
    #if ENABLE_XPU_VIOLATION
        scm_xpu_err_fatal_init();
    #endif
    }
               target_init();  //各种板子资源初始化,mmc,sdc,usb,volumn等---->
                  //里面最重要的是mmc的初始化   target_sdc_init();
                  //还有RPM                                  rpm_smd_init();

                dprintf(SPEW, "calling apps_init() ");//app初始化以及启动app
                apps_init();//开始执行app/aboot.c中的aboot_init函数
    }

    接下来开始执行app/aboot/aboot.c
    在amboot.c的源码最底端:
    APP_START(aboot)     //可以看出上述的app启动的第一个就是aboot_init
        .init = aboot_init,
    APP_END

    /* each app needs to define one of these to define its startup conditions */每个app需要的定义
    struct app_descriptor {
        const char *name;
        app_init  init;
        app_entry entry;
        unsigned int flags;
    };
    开始研究aboot_init函数:
    void aboot_init(const struct app_descriptor *app)
    {
              /* Setup page size information for nv storage */首先判断从哪启动emmc还是flash
        if (target_is_emmc_boot())
        {
            page_size = mmc_page_size();
            page_mask = page_size - 1;
        }
        else
        {
            page_size = flash_page_size();
            page_mask = page_size - 1;
        }
            read_device_info(&device);      //读取设备信息

        /* Display splash screen if enabled */
    #if DISPLAY_SPLASH_SCREEN                           //初始化显示屏
        dprintf(SPEW, "Display Init: Start ");
        target_display_init(device.display_panel);
        dprintf(SPEW, "Display Init: Done ");
    #endif


        target_serialno((unsigned char *) sn_buf);   //获取串口号
        dprintf(SPEW,"serial number: %s ",sn_buf);

          memset(display_panel_buf, '', MAX_PANEL_BUF_SIZE);
        /*如果用户强制重启是进入正常模式的,不会进入fastboot模式,然而在实现中该函数返回0,不执行
         * Check power off reason if user force reset,
         * if yes phone will do normal boot.
         */
        if (is_user_force_reset())
            goto normal_boot;

        接下来就做一些除了boot up之外的一些事情,这里面主要判断组合按键,其中可以进入dload(livesuit)模式和recovery模式
         其中recovery模式进入Linux内核,启动recovery映像,通过界面选择烧写的软件包update.zip
    注:android镜像烧写总共有三种:fastboot(调试用),livesuit(下载整个镜像),recovery(启动recovery镜像)

        然后判断是正常启动还是非正常启动,如果正常启动就recovery_init然后直接启动内核(包括传参)
    两种情况:emmc和flash启动
                if (target_is_emmc_boot())
            {
                if(emmc_recovery_init())
                    dprintf(ALWAYS,"error in emmc_recovery_init ");
                if(target_use_signed_kernel())
                {
                    if((device.is_unlocked) || (device.is_tampered))
                    {
                    #ifdef TZ_TAMPER_FUSE
                        set_tamper_fuse_cmd();
                    #endif
                    #if USE_PCOM_SECBOOT
                        set_tamper_flag(device.is_tampered);
                    #endif
                    }
                }

                boot_linux_from_mmc();    ---->lk的启动画面也在里面,其实就是完成启动前的最后准备工作
            }
            else
            {
                recovery_init();
        #if USE_PCOM_SECBOOT
            if((device.is_unlocked) || (device.is_tampered))
                set_tamper_flag(device.is_tampered);
        #endif
                boot_linux_from_flash();
            }

        如果是非正常启动就进入fastboot模式,之前进行fastboot命令的注册以及启动fastboot
        /* register aboot specific fastboot commands */注册fastboot命令
        aboot_fastboot_register_commands();

        /* dump partition table for debug info */
        partition_dump();

        /* initialize and start fastboot */初始化fastboot以及启动
        fastboot_init(target_get_scratch_address(), target_get_max_flash_size());
    }
    现在分析fastboot_init函数做些什么工作:
    int fastboot_init(void *base, unsigned size)
    {
        /* target specific initialization before going into fastboot. */进入fastboot前的目标板初始化
        target_fastboot_init();

             /* setup serialno */创建串口号
        target_serialno((unsigned char *) sn_buf);
        dprintf(SPEW,"serial number: %s ",sn_buf);
        surf_udc_device.serialno = sn_buf;

            /* initialize udc functions to use dwc controller */初始化usb控制器,因为fastboot和板子通过usb进行通信
             /* register udc device */注册usb controller设备

             /* register gadget */注册gadget

    thr = thread_create("fastboot", fastboot_handler, 0, DEFAULT_PRIORITY, 4096);//创建线程
                              ---->static int fastboot_handler(void *arg)
    {
        for (;;) {
            event_wait(&usb_online);//等待usb连接
            fastboot_command_loop();//循环处理fastboot命令
        }
        return 0;
    }
    }
    大多数fastboot命令cmd_xxx是在aboot.c中实现的,然后进行注册

    现在分析 boot_linux_from_mmc函数做些什么工作:
    常用结构体:
    struct boot_img_hdr
    {
        unsigned char magic[BOOT_MAGIC_SIZE];

        unsigned kernel_size;  /* size in bytes */
        unsigned kernel_addr;  /* physical load addr */

        unsigned ramdisk_size; /* size in bytes */
        unsigned ramdisk_addr; /* physical load addr */

        unsigned second_size;  /* size in bytes */
        unsigned second_addr;  /* physical load addr */

        unsigned tags_addr;    /* physical addr for kernel tags */
        unsigned page_size;    /* flash page size we assume */
        unsigned dt_size;      /* device_tree in bytes */
        unsigned unused;    /* future expansion: should be 0 */

        unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */

        unsigned char cmdline[BOOT_ARGS_SIZE];    //串口的传参在这里

        unsigned id[8]; /* timestamp / checksum / sha1 / etc */
    };

    现在研究串口cmdline在哪打印的:
    void boot_linux(void *kernel, unsigned *tags,
            const char *cmdline, unsigned machtype,
            void *ramdisk, unsigned ramdisk_size)
    {
            final_cmdline = update_cmdline((const char*)cmdline);

    }

    void aboot_init(const struct app_descriptor *app)
    {
            bool boot_into_fastboot = false;    //判断是否进入fastboot模式
    #if UART_INPUT_INTO_FASTBOOT
        char getc_value;
    #endif

    #if UART_INPUT_INTO_FASTBOOT    //经测验,按键f要一直按住,否则很难检测到,后续可以考虑延迟一段时间
                if(dgetc(&getc_value, 0) >= 0) {
                    if(getc_value == 'f') {
                        boot_into_fastboot = true;
                        dprintf(INFO,"keyboard is pressed, goto fastboot mode! ");
                    }
                }
    #endif

    }

  • 相关阅读:
    turtle 绘制爱心
    数据库总结
    anconda安装使用
    爬虫之存储库MongoDB
    【python】代码换行的几种方法
    【python】 合并列表的方法
    【notebook】常用在线notebook总结
    【PDF】PDF无法注释的一种解决方案
    【课程】MIT深度学习课程:架起理论与实践的桥梁
    【今日CV 视觉论文速览】Thu, 21 Feb 2019
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/7771465.html
Copyright © 2011-2022 走看看