Jimmy Chen

A Programmer

(原创)高通SBL1源码初步分析

最近工作从MTK平台转移到高通平台,然后先研究了下高通平台的启动流程,高通平台的启动流程如下截图(截图来源于网络)

启动流程

《(原创)高通SBL1源码初步分析》

  从图中总结启动流程如下:

  1. AP侧CPU上电
  2. 在Cortex-A53芯片内部ROM的PBL首先运行,PBL会从boot device中加载并验证SBL1到TCM中。这里的TCM可以理解为CPU的二级缓存。这里做一个猜测,既然PBL能够从boot device中加载SBL1,那PBL应该是初始化过boot device的。
  3. SBL1初始化DDR,并从boot device中加载并且校验如下镜像
    • QSEE或者TZ镜像
    • QHEE镜像
    • RPM_FW镜像
    • APPSBL
  4. SBL1加载并验证完上述镜像后,即将执行权转移到QSEE中,QSEE将设置并初始化一个安全的执行环境。
  5. QSEE通知RPM去执行RPM_FW相关代码
  6. QSEE将执行权转移到APPSBL中,APPSBL也就是我们常说的LK了
  7. LK加载HLOS的kernel,HLOS加载并校验MBA并启动AMSS的modem等镜像

基本的启动流程就这样。总的来说,感觉启动流程和MTK芯片的启动流程有点类似。也是从芯片内部的ROM开始,然后初始化外部设备加载校验TZ,modem之类的镜像,在之后就是执行LK。

代码简单分析

  代码路径为boot_images/core/boot/secboot3/hw/msm8952/sbl1/sbl1.S

首先是导入一些外部符号,这里重要的一个函数是sbl1_main_ctl,最后会跳转到这个函数继续执行。

    IMPORT |Image$$SBL1_SVC_STACK$$ZI$$Limit|
    IMPORT |Image$$SBL1_UND_STACK$$ZI$$Limit|
    IMPORT |Image$$SBL1_ABT_STACK$$ZI$$Limit|
    IMPORT boot_undefined_instruction_c_handler
    IMPORT boot_swi_c_handler
    IMPORT boot_prefetch_abort_c_handler
    IMPORT boot_data_abort_c_handler
    IMPORT boot_reserved_c_handler
    IMPORT boot_irq_c_handler
    IMPORT boot_fiq_c_handler
    IMPORT boot_nested_exception_c_handler
    IMPORT sbl1_main_ctl
    IMPORT boot_crash_dump_regs_ptr

导入需要使用的外部符号后,真正的执行代码是从sbl1_entry处开始。首先会保存一些从PBL传过来的参数,设置中断运行模式等,最后跳转到sbl1_main_ctl执行。

/* 计算SBL启动的时间 */
sbl_start_time = CALCULATE_TIMESTAMP(HWIO_IN(TIMETICK_CLK));
boot_clock_debug_init();
/* Enter debug mode if debug cookie is set */
sbl1_debug_mode_enter();  
/* Initialize the stack protection canary */
boot_init_stack_chk_canary();

/* 分配用于记录BootLoader启动过程中用到的参数 */
boot_shared_imem_init(&bl_shared_data);
/* 初始化RAM */
boot_ram_init(&sbl1_ram_init_data);
/* 初始化log系统,即串口驱动 */
sbl1_boot_logger_init(&boot_log_data, pbl_shared);
/* 检索PBL传递过来的数据 */ 
sbl1_retrieve_shared_info_from_pbl(pbl_shared); 
/* Initialize the QSEE interface */
sbl1_init_sbl_qsee_interface(&bl_shared_data,&sbl_verified_info);
/* Initialize SBL memory map. Initializing early because drivers could be located in RPM Code RAM. */
sbl1_populate_initial_mem_map(&bl_shared_data);
/* 初始化DAL*/
boot_DALSYS_InitMod(NULL); 
/* 配置PMIC芯片,以便我们能通过PS_HOLD复位 */
sbl1_hw_init();
/* 执行sbl1的目标依赖进程 */
boot_config_process_bl(&bl_shared_data, SBL1_IMG, sbl1_config_table);

sbl1_config_table记录在加载后续镜像是需要执行的步骤以及参数,下面简单看一下:

boot_configuration_table_entry sbl1_config_table[] = 
{

 /* SBL1 -> QSEE */
  {
    SBL1_IMG,                   /* host_img_id */
    CONFIG_IMG_QC,              /* host_img_type */
    GEN_IMG,                    /* target_img_id */
    CONFIG_IMG_ELF,             /* target_img_type */
    SECBOOT_QSEE_SW_TYPE,       /* target_img_sec_type */ 
    ..........
    load_qsee_pre_procs,        /* pre_procs */ 
    load_qsee_post_procs,       /* post_procs */
    ..........
    TRUE,                       /* enable_rollback_protection*/
    FALSE,                      /* enable_xpu */
    0x0                         /* xpu_proc_id*/    
  },

load_qsee_pre_procs

load_qsee_pre_procs也是一个数组,记录要执行的函数:

/* 保存reset寄存器的内容 */
boot_save_reset_register_log,

/* 初始化flash存储设备 */
boot_flash_init,

/* 从EEPROM中加载CDT表数据 */
boot_config_data_table_init,

/* 保存平台ID */
sbl1_hw_platform_pre_ddr,

/* 根据CDT表的数据来配置DDR */
sbl1_ddr_set_params,

/* 初始化DDR */
(boot_procedure_func_type)sbl1_ddr_init,

/*----------------------------------------------------------------------
 Run deviceprogrammer if compiling the deviceprogrammer_ddr image.
----------------------------------------------------------------------*/
boot_deviceprogrammer_ddr_main,

/* 初始化SBL1 DDR ZI区域, 将boot log保存到DDR中 */   
sbl1_post_ddr_init,

/* 此函数挺重要,我能改到的东西基本上都基于它,所有的PMIC API都是在此函数调用boot_pm_dirver_init()之后再被调用*/
sbl1_hw_init_secondary, 
 
/* DDR training */
(boot_procedure_func_type)sbl1_wait_for_ddr_training,

/* 初始化SBL1 DDR ZI区域, 将页表保存到DDR中 */
sbl1_post_ddr_training_init, 

/* 清空安全代码执行所需的区域 */
sbl1_cleanse_security_regions,

/* Backup QSEE and QHEE region for ramdumps taken after SBL has executed */
boot_dload_dump_security_regions,

/* Check to see if DLOAD mode needs to be entered */
boot_dload_check,

/* Last entry in the table. */
NULL

load_qsee_post_procs

load_qsee_post_procs也是一个数据,记录在加载QSEE镜像后需要完成的工作

  /* 初始化secure watchdog */
  boot_secure_watchdog_init,
  
  /* Load SEC partition if it exists.  This must be done after QSEE is
     loaded as the partition is loaded into a QSEE buffer. */
  sbl1_load_sec_partition,  

  /* Set the memory barrier pointer to shared memory */
  boot_cache_set_memory_barrier,

  /*----------------------------------------------------------------------
   Put SMEM in debug state such that smem_alloc() calls will return NULL. 
   The state is changed back to normal once smem_boot_init() is called.
   This call has to be made after setting the memory barrier.
  ----------------------------------------------------------------------*/
  boot_smem_debug_init,  
    
  /* Initialize shared memory after dload to preserve logs */
  boot_smem_init,

#if !defined(FEATURE_RUMI_BOOT)
  /* Stub out for rumi build. pmic api  pm_get_power_on_status gets 
     called from below api to get power on reason */ 
  /*----------------------------------------------------------------------
   Store Power on Status in SMEM. 
   Needs to be done after PMIC and SMEM initialization
  ----------------------------------------------------------------------*/
  boot_smem_store_pon_status,
#endif  

  /*----------------------------------------------------------------------
   Store the platform id to smem
  ----------------------------------------------------------------------*/
  sbl1_hw_platform_smem,
   
  /*----------------------------------------------------------------------
   Get shared data out of the flash device module
  ----------------------------------------------------------------------*/
  boot_share_flash_data,
  
  /*----------------------------------------------------------------------
   populate the ram partition table
  ----------------------------------------------------------------------*/
  boot_populate_ram_partition_table,

  /*----------------------------------------------------------------------
   Initialize GPIO for low power configuration
  ----------------------------------------------------------------------*/
  sbl1_tlmm_init,
  
  /*-----------------------------------------------------------------------
   Calls efs cookie handling api to perform efs backup/restore
  -----------------------------------------------------------------------*/  
  sbl1_efs_handle_cookies,
    
  /*-----------------------------------------------------------------------
   APT Security Test
   ----------------------------------------------------------------------*/
  (boot_procedure_func_type)boot_apt_test,

  /* Last entry in the table. */
  NULL

boot_config_process_bl

boot_config_process_bl函数就是根据boot_config_process_bl数组内的内容逐个调用boot_config_process_entry来加载完成,另外还要将保存在bl_shared_data里面的参数传递过去供启动的时候使用。

void boot_config_process_bl 
( 
  bl_shared_data_type *bl_shared_data, 
  image_type host_img, 
  boot_configuration_table_entry * boot_config_table 
)
{
  boot_configuration_table_entry *curr_entry = NULL;

  BL_VERIFY( bl_shared_data != NULL && boot_config_table != NULL,
             BL_ERR_NULL_PTR_PASSED|BL_ERROR_GROUP_BOOT);

  /* For every entry in the boot configuration table */
  for(curr_entry = boot_config_table;
      curr_entry->host_img_id != NONE_IMG;
      curr_entry++)
  {
    /* Process entries sequentially only for the specific host_img */
    if(curr_entry->host_img_id == host_img)
    {
      boot_config_process_entry(bl_shared_data,
                                curr_entry);
    }
  }
 
  return;
}

断断续续的写了好久,一方面刚刚接触还不够熟悉,另一方面是因为高通license的问题,也不知道放这些代码会不会被查水表。就这样吧,就当做简单的源码阅读笔记好了。以后再项目中遇到问题再抓出来分析分析好了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d 博主赞过: