SBL1启动

来源:互联网 发布:行知教育集团 编辑:程序博客网 时间:2024/04/28 16:55
sbl1是平台相关的启动程序,除了执行必要的初始化操作,还负责加载PMIC,QSEE(TZ),RPM等Firmware,然后跳转到APPSBL执行(XBL跳转到XBL Core,也就是UEFI入口执行);

1. sbl1_config_table

所有在sbl1加载的image,都保存在sbl1_config_table[]数组中,这是一个配置表,记录Image加载前后的行为,boot_configuration_table_entry结构体定义如下:

@auth和@load的值为TRUE,表示加载并校验Image;
@boot_load_cancel函数检查是否要加载Image;
@target_img_partition_id表示Image所在分区的GUID值;
如果要立即执行Image,设置@exec值及@exec_func执行函数;
如果要跳转到Image执行,设置@jump值及@jump_func跳转函数,如APPSBL跳转函数;
FW加载前可能需要一些准备工作,可以放在@pre_procs函数列表,FW加载后会执行@post_procs函数列表;

typedef struct
{
  image_type host_img_id;                 /**< Image ID of the host */
  config_image_type host_img_type;        /**< Image type of the host */
  image_type target_img_id;               /**< Image ID of the target */
  config_image_type target_img_type;      /**< Image type of the target */
  secboot_sw_type target_img_sec_type;    /**< Secure Software ID of the target */
  boot_boolean load;                      /**< Target will be loaded */
  boot_boolean auth;                      /**< Target will be authenticated */
  boot_boolean exec;                      /**< Target will execute immediately after
                                               loading/authentication */
  boot_boolean jump;                      /**< Target will be jumped to after
                                               post procedures are complete */
  boot_boolean recovery;                  /**< Target image partiton will be restored from
                                               backup image partition once loading primary
                                               image fails */
  boot_procedure_func_type exec_func;     /**< Function pointer to execute function.
                                               Must be defined if exec is TRUE */
  boot_procedure_func_type jump_func;     /**< Function pointer to jump function.
                                               Must be defined if jump is TRUE */
  boot_procedure_func_type *pre_procs;    /**< Function pointer array to pre-procedures */
  boot_procedure_func_type *post_procs;   /**< Function pointer array to post-procedures */
  boot_logical_func_type boot_load_cancel;/**< Function pointer to cancel loading */
  uint8 * target_img_partition_id;        /**< Target image partition ID information */
  uint8 * backup_img_partition_id;        /**< Target image partition ID information */
  uint8 * target_img_str;                  /**< Target image name information  */
  whitelist_region_type * whitelist_table; /**< Whitelist table */
  boot_boolean boot_ssa_enabled;           /**< Subsystem self authentication (ssa)*/
  boot_boolean enable_rollback_protection; /**< Enable Roll back protection feature or not */
  boot_boolean enable_xpu;                 /**< Enable XPU configuration for the image */
  uint32 xpu_proc_id;                      /**< XPU proc id to be passed to TZ */
  uint32 RESERVED_1;                      /**< RESERVED */
  uint32 RESERVED_2;                      /**< RESERVED */
  uint32 RESERVED_3;                      /**< RESERVED */
  uint32 RESERVED_4;                      /**< RESERVED */
}boot_configuration_table_entry;

通常只有ABL和APPSBL Image需要跳转执行,其它Image直接加载就可以了;

2. Hash校验

高通在elf格式的映像文件中,增加了一个Hash段,用于校验Image的完整性,所以在加载Image前,要先把Hash段加载到内存中;
这里有两个问题,即如何从Storage读取数据?以及如何识别Hash段?

boot_flash_dev_if.h定义Image打开和关闭方法:

void boot_flash_configure_target_image
(
  uint8 * partition_id                     /* Target image partition ID */
)

boot_flash_trans_if_type* boot_flash_dev_open_image
(
  image_type image_id                   /* Image type to open */
)

void boot_flash_dev_close_image
(
  boot_flash_trans_if_type **p_trans_if
)

boot_flash_trans_if.h定义Image读方法:

boolean boot_flash_trans_read
(
  struct  boot_flash_trans_if_type *trans_if,  /* Pointer to the translation interface */
  void   *dest_addr,                      /* Destination address */
  uint32  byte_offset,                    /* Logical source byte address */
  uint32  size                            /* Size to copy in bytes */
)

最后是内存保护,保护临时存储区不会被覆盖:

void boot_clobber_add_local_hole
(
  boot_clobber_local_type *clobber_tbl_ptr,  /* Pointer to the local table */
  const void *start_addr,  /* Start address of the local hole to add */
  uint32 size              /* Size of the local hole to add in bytes */
)

void boot_clobber_remove_local_hole
(
  boot_clobber_local_type *clobber_tbl_ptr,  /* Pointer to the local table */
  const void *start_addr,  /* Start address of the local hole to remove */
  uint32 size              /* Size of the local hole to remove in bytes */
)

下面用一个简单的示例说明如何读取Image数据:

/* Flash translation interface */
boot_flash_trans_if_type * trans_if = NULL;

/* Configure the target image using custom partition ID information */
boot_flash_configure_target_image(partition_id);

... ...
trans_if = boot_flash_dev_open_image(GEN_IMG);
... ...
boot_clobber_add_local_hole( boot_flash_trans_get_clobber_tbl_ptr( trans_if ),
                             &ehdr, sizeof(Elf64_Ehdr) );

/* Copy the ELF header from flash into our local structure */
success = boot_flash_trans_read( trans_if,
                                 &ehdr,
                                 0,
                                 sizeof(Elf64_Ehdr));

/* Remove local hole */
boot_clobber_remove_local_hole( boot_flash_trans_get_clobber_tbl_ptr( trans_if ),
                                &ehdr, sizeof(Elf64_Ehdr) );

如何识别Hash段呢?这里需要解析elf文件,elf header保存在全局变量ehdr_ptr,program header保存在全局变量phdr_array,Program Header的偏移量保存在ehdr_phoff

boot_elf_open_and_parse_elf_header(partition_id, header_ptr);

/* Allow the address range of the program header array to be written to */
boot_clobber_add_local_hole( boot_flash_trans_get_clobber_tbl_ptr( trans_if ),
                             phdr_array_ptr, phdr_array_size);

/* Load the program headers */
success = boot_flash_trans_read( trans_if,
                                 phdr_array_ptr,
                                 (uint32)ehdr_phoff,
                                 phdr_array_size);

检查Segment Flag标志位,定位Hash段:

/* Locate hash segment */
for(index = 0; index < phdr_num; index++)
{
    /* Check for the hash segment program header to parse QC header */
    if( MI_PBT_SEGMENT_TYPE_VALUE(phdr_flags) == MI_PBT_HASH_SEGMENT)
    ... ...

读取QC header和Hash数据:

/* Read in the QC header once hash segment is found */
success = boot_flash_trans_read( trans_if,
                                 header_ptr,
                                 (uint32)phdr_offset,
                                 sizeof(*header_ptr) );
... ...
/* Load full hash segment into memory */
boot_elf_load_seg_generic(phdr_entry_ptr, hash_segment_base_ptr);
... ...
/* After hash segment is loaded into memory, assign start address of hash entries */
hash_segment_start = (uint8 *)(hash_segment_base_ptr + sizeof(*header_ptr));

至此Hash数据段已经全部load到内存中,这样如果@auth值为TRUE,那么在加载Image前,需要先进行Hash校验,下面是校验元数据:

/* For ELF images, authenticate the hash data segment first before loading any segments */
if(boot_config_entry->auth == TRUE)
{
   //load elf image info
   error_status = boot_populate_sbl_info(&sbl_elf_info, &sbl_white_list_param,
                                         boot_config_entry->whitelist_table);
   BL_VERIFY( error_status == BL_ERR_NONE, error_status );

   /* Authenticate the image metadata (hashtable)*/
   error_status = boot_auth_image_hashtable((uint32)boot_config_entry->target_img_sec_type,
                                            &sbl_elf_info,
                                            &sbl_white_list_param
                                            );
   ... ...
 
加载和校验段数据,数据将会被加载到Program Header指定的物理地址:

/* Load the ELF segments and verify segments hash */
boot_load_elf_image((uint32)boot_config_entry->target_img_sec_type);

下面以RPM为例,代码段将会被加载到0x200000,数据段被加载到0x290000;

# readelf -l rpm.mbn

Elf file type is EXEC (Executable file)
Entry point 0x9
There are 4 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  NULL           0x000000 0x00000000 0x00000000 0x000b4 0x00000     0
  NULL           0x001000 0x0029c000 0x0029c000 0x019a8 0x02000     0x1000
  LOAD           0x003000 0x00000000 0x00200000 0x1e720 0x1e720 RWE 0x8
  LOAD           0x021720 0x00090000 0x00290000 0x0bec0 0x0bf00 RW  0x20

3. 执行跳转命令

Image加载完成后,执行跳转函数,

/* Executing QSEE from SBL1 */
static void qsee_jump_func(bl_shared_data_type *bl_shared_data )
{
    /* Search image entries in QSEE interface for QSEE and get entry point.
    The entry point and elf type of QSEE must be stored so if an abnormal
    reset occurs the system debug image knows how to get to QSEE.  A small
    section has been carved out in the system debug image memory for this
    data. */
    qsee_info->entry_point =
    boot_sbl_qsee_interface_get_entry_point(&(bl_shared_data->sbl_qsee_interface),
                                            SECBOOT_QSEE_SW_TYPE);
    qsee_info->elf_class =
    boot_sbl_qsee_interface_get_eident(&(bl_shared_data->sbl_qsee_interface),
                                       SECBOOT_QSEE_SW_TYPE);
    ... ...
    ((void (*)())(uint32)(qsee_info->entry_point)) (&(bl_shared_data->sbl_qsee_interface));

原创粉丝点击