栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

【SemiDrive源码分析】【X9芯片启动流程】27 - AP1 Android Preloader启动流程分析(加载atf、tos、bootloader镜像后进入BL31环境)

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【SemiDrive源码分析】【X9芯片启动流程】27 - AP1 Android Preloader启动流程分析(加载atf、tos、bootloader镜像后进入BL31环境)

【SemiDrive源码分析】【X9芯片启动流程】27 - AP1 Android Preloader启动流程分析(加载atf、tos、bootloader镜像后进入BL31环境)
  • 一、Android Preloader
    • 1.1 汇编入口 start.S:跳转 lk_main() 函数
    • 1.2 lk_main() 函数:创建并运行 bootstrap2 线程
      • 1.2.1 任务段 .lk_init 分析
    • 1.3 bootstrap2 线程:解析并执行.app段中的preloader app
      • 1.3.1 应用段 .apps 分析
      • 1.3.2 启动shell app
        • 1.3.2.1 shell app中支持的所有命令
      • 1.3.3 启动preloader app:
      • 1.3.4 preloader_configs 解析
      • 1.3.5 AP1 启动镜像加载:启动 bootloader
      • 1.3.6 smc跳转进入 BL31环境


本 SemiDrive源码分析 之 Yocto源码分析 系列文章汇总如下:

  1. 《【SemiDrive源码分析】【Yocto源码分析】01 - yocto/base目录源码分析(编译环境初始化流程)》
  2. 《【SemiDrive源码分析】【Yocto源码分析】02 - yocto/meta-openembedded目录源码分析》
  3. 《【SemiDrive源码分析】【Yocto源码分析】03 - yocto/meta-semidrive目录及Yocto Kernel编译过程分析(上)》
  4. 《【SemiDrive源码分析】【Yocto源码分析】04 - yocto/meta-semidrive目录及Yocto Kernel编译过程分析(下)》
  5. 《【SemiDrive源码分析】【Yocto源码分析】05 - 找一找Yocto Kernel编译过程中所有Task的源码在哪定义的呢?》
  6. 《【SemiDrive源码分析】【Yocto源码分析】06 - Kernel编译生成的Image.bin、Image_nobt.dtb、modules.tgz 这三个文件分别是如何生成的?》
  7. 《【SemiDrive源码分析】【Yocto源码分析】07 - core-image-base-x9h_ref_serdes.rootfs.ext4 文件系统是如何生成的》
  8. 《【SemiDrive源码分析】【X9芯片启动流程】08 - X9平台 lk 目录源码分析 之 目录介绍》
  9. 《【SemiDrive源码分析】【X9芯片启动流程】09 - X9平台系统启动流程分析》
  10. 《【SemiDrive源码分析】【X9芯片启动流程】10 - BareMetal_Suite目录R5 DIL.bin 引导程序源代码分析》
  11. 《【SemiDrive源码分析】【X9芯片启动流程】11 - freertos_safetyos目录Cortex-R5 DIL2.bin 引导程序源代码分析》
  12. 《【SemiDrive源码分析】【X9芯片启动流程】12 - freertos_safetyos目录Cortex-R5 DIL2.bin 之 sdm_display_init 显示初始化源码分析》
  13. 《【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法》
  14. 《【SemiDrive源码分析】【X9芯片启动流程】14 - freertos_safetyos目录Cortex-R5 SafetyOS/RTOS工作流程分析》
  15. 《【SemiDrive源码分析】【X9芯片启动流程】15 - freertos_safetyos目录 R5 SafetyOS 之 tcpip_init() 代码流程分析》
  16. 《【SemiDrive源码分析】【X9 Audio音频模块分析】16 - 音频模块框图及硬件原理图分析》
  17. 《【SemiDrive源码分析】【X9芯片启动流程】17 - R5 SafetyOS 之 LK_INIT_LEVEL_PLATFORM 阶段代码流程分析(上)dcf_init 核间通信初始化》
  18. 《【SemiDrive源码分析】【X9芯片启动流程】18 - R5 SafetyOS 之 LK_INIT_LEVEL_PLATFORM 阶段代码流程(下)启动QNX、Android》
  19. 《【SemiDrive源码分析】【X9芯片启动流程】19 - MailBox 核间通信机制介绍(理论篇)》
  20. 《【SemiDrive源码分析】【X9芯片启动流程】20 - MailBox 核间通信机制介绍(代码分析篇)之 MailBox for RTOS 篇》
  21. 《【SemiDrive源码分析】【X9芯片启动流程】21 - MailBox 核间通信机制介绍(代码分析篇)之 Mailbox for Linux 篇》
  22. 《【SemiDrive源码分析】【X9芯片启动流程】22 - MailBox 核间通信机制介绍(代码分析篇)之 RPMSG-VIRTIO Kernel 篇》
  23. 《【SemiDrive源码分析】【X9芯片启动流程】23 - MailBox 核间通信机制介绍(代码分析篇)之 RPMSG-IPCC Kernel 篇》
  24. 《【SemiDrive源码分析】【X9芯片启动流程】24 - MailBox 核间通信机制相关寄存器介绍》
  25. 《【SemiDrive源码分析】【X9芯片启动流程】25 - MailBox 核间通信机制介绍(代码分析篇)之 RPMSG-IPCC RTOS & QNX篇》
  26. 《【SemiDrive源码分析】【X9芯片启动流程】26 - R5 SafetyOS 之 LK_INIT_LEVEL_TARGET 阶段代码流程分析(TP Drvier、Audio Server初始化)》
  27. 《【SemiDrive源码分析】【X9芯片启动流程】27 - AP1 Android Preloader启动流程分析(加载atf、tos、bootloader镜像后进入BL31环境)》
  28. 《【SemiDrive源码分析】【X9芯片启动流程】28 - MailBox 核间通信机制介绍(代码分析篇)之 Property篇》
  29. 《【SemiDrive源码分析】【X9芯片启动流程】29 - MailBox 核间通信机制介绍(代码分析篇)之 RPCall篇》
  30. 《【SemiDrive源码分析】【X9芯片启动流程】30 - MailBox 核间通信机制介绍(代码分析篇)之 Notify篇》
  31. 《【SemiDrive源码分析】【X9芯片启动流程】31 - MailBox 核间通信机制介绍(代码分析篇)之 Socket篇》
  32. 《【SemiDrive源码分析】【X9芯片启动流程】32 - MailBox 核间通信机制介绍(代码分析篇)之 /dev/vircan篇》


从《【SemiDrive源码分析】【X9芯片启动流程】18 - R5 SafetyOS 之 LK_INIT_LEVEL_PLATFORM 阶段代码流程(下)启动QNX、Android》 中,
我们知道 R5是通过调用 reboot_ap1() 来启动 android preloader 的,那本文,我们正式进入 android 分析 preloader 源码。


一、Android Preloader 1.1 汇编入口 start.S:跳转 lk_main() 函数

从 rtoslk_bootbuild-preloader_x9_plus_ref_serdessystem-onesegment-sd.ld 中可以看入,
android preloader 的入口函数为 ENTRY(_start):

# buildsystemrtoslk_bootbuild-preloader_x9_plus_ref_serdessystem-onesegment-sd.ld
OUTPUT_FORMAT("elf64-littleaarch64", "elf64-bigaarch64", "elf64-littleaarch64")
OUTPUT_ARCH(aarch64)

ENTRY(_start)
SECTIONS
{
    . = 0xffff000000000000 + 0;
    . = 0xffff000000000000 + 0x57A00000;

ENTRY(_start) 对应的代码如下:
如下去除了一些不会跑的代码,有兴趣的兄弟自行分析下,汇编启动代码看了太多了,都快能背下来干了啥了,
我就不看了,直接坐灰机跳到 lk_main() 中。

# buildsystemrtoslkarcharm64start.S
FUNCTION(_start)
.globl arm_reset
arm_reset:
    bl      arch_invalidate_dcache_all
    mrs     x9, currentel
    lsr     x9, x9, #2
    cmp     w9, #3
    bne     1f
    adrp    tmp, arm64_exception_base_el3
    msr     vbar_el3, tmp
    b       2f
1:
    cmp     w9, #2
    bne     2f
    adrp    tmp, arm64_exception_base_el2
    msr     vbar_el2, tmp
2:  bl      arm64_elX_to_el1

    
    ic ialluis
    isb
    
    mrs     tmp, sctlr_el1
    orr     tmp, tmp, #(1<<12) 
    orr     tmp, tmp, #(1<<2)  
    orr     tmp, tmp, #(1<<3)  
    orr     tmp, tmp, #(1<<4)  
    bic     tmp, tmp, #(1<<1)  
    msr     sctlr_el1, tmp
    

    
    adrp    page_table1, arm64_kernel_translation_table
    add     page_table1, page_table1, #:lo12:arm64_kernel_translation_table
    
    
    adrp    page_table0, tt_trampoline
    add     page_table0, page_table0, #:lo12:tt_trampoline
    mov     tmp, #0
    
.Lclear_top_page_table_loop:
    str     xzr, [page_table1, tmp, lsl #3]
    add     tmp, tmp, #1
    cmp     tmp, #MMU_KERNEL_PAGE_TABLE_ENTRIES_TOP
    bne     .Lclear_top_page_table_loop
    
    adrp    mmu_initial_mapping, mmu_initial_mappings
    add     mmu_initial_mapping, mmu_initial_mapping, #:lo12:mmu_initial_mappings
.Linitial_mapping_loop:

    ldp     paddr, vaddr, [mmu_initial_mapping, #__MMU_INITIAL_MAPPING_PHYS_OFFSET]
    ldp     size, tmp, [mmu_initial_mapping, #__MMU_INITIAL_MAPPING_SIZE_OFFSET]
    tbzmask tmp, MMU_INITIAL_MAPPING_FLAG_DYNAMIC, .Lnot_dynamic
    adr     paddr, _start
    mov     size, x0
    str     paddr, [mmu_initial_mapping, #__MMU_INITIAL_MAPPING_PHYS_OFFSET]
    str     size, [mmu_initial_mapping, #__MMU_INITIAL_MAPPING_SIZE_OFFSET]
.Lnot_dynamic:
    
    cbz     size, .Linitial_mapping_done
    mov     mapping_size, size
    
    tbzmask tmp, MMU_INITIAL_MAPPING_FLAG_UNCACHED, .Lnot_uncached
    ldr     attr, =MMU_INITIAL_MAP_STRONGLY_ORDERED
    b       .Lmem_type_done
.Linitial_mapping_done:
    
    
    mov     tmp, #0
.Lclear_tt_trampoline:
    str     xzr, [page_table0, tmp, lsl#3]
    add     tmp, tmp, #1
    cmp     tmp, #MMU_PAGE_TABLE_ENTRIES_IDENT
    blt     .Lclear_tt_trampoline

    
    adr     tmp, .Lmmu_on_pc
    lsr     tmp, tmp, #MMU_IDENT_TOP_SHIFT    
    ldr     tmp2, =MMU_PTE_IDENT_FLAGS
    add     tmp2, tmp2, tmp, lsl #MMU_IDENT_TOP_SHIFT  

    str     tmp2, [page_table0, tmp, lsl #3]     

    
    
    tlbi    vmalle1is
    isb
    dsb     sy
    
    ldr     tmp, =MMU_MAIR_VAL
    msr     mair_el1, tmp
    
    
    
    ldr     tmp, =MMU_TCR_FLAGS_IDENT
    msr     tcr_el1, tmp
    isb
    
    msr     ttbr0_el1, page_table0
    msr     ttbr1_el1, page_table1
    isb
    
    mrs     tmp, sctlr_el1
    
    orr     tmp, tmp, #0x1
    
    msr     sctlr_el1, tmp
.Lmmu_on_pc:
    isb
    
    ldr     tmp, =.Lmmu_on_vaddr
    br      tmp

.Lmmu_on_vaddr:
    
    ldr     tmp, =MMU_TCR_FLAGS_KERNEL
    msr     tcr_el1, tmp
    isb
    
    tlbi    vmalle1
    isb

    ldr tmp, =__stack_end
    mov sp, tmp
.L__do_bss:
    ldr     tmp, =__post_prebss_bss_start
    ldr     tmp2, =__bss_end
    sub     tmp2, tmp2, tmp
    cbz     tmp2, .L__bss_loop_done
.L__bss_loop_done:

    bl  lk_main
    b   .

1.2 lk_main() 函数:创建并运行 bootstrap2 线程

主要工作如下:

  1. 配置当前线程为bootstrap,优先级为 HIGHEST_PRIORITY
  2. 触发 LK_INIT_LEVEL_EARLIEST 到 LK_INIT_LEVEL_ARCH_EARLY - 1等级的任务运行
  3. 切换 CPU 从EL3 到 EL1,初始化 MMU
  4. 触发 LK_INIT_LEVEL_ARCH_EARLY到 LK_INIT_LEVEL_PLATFORM_EARLY- 1等级的任务运行
  5. 串口初始化、gic中断初始、通过psci_call 启动剩余的CPU
  6. 触发 LK_INIT_LEVEL_PLATFORM_EARLY到 LK_INIT_LEVEL_TARGET_EARLY - 1等级的任务运行
  7. 触发 LK_INIT_LEVEL_TARGET_EARLY到 LK_INIT_LEVEL_HEAP - 1等级的任务运行
  8. 初始化堆Heap
  9. 运行.ctors段中的函数
  10. 触发 LK_INIT_LEVEL_HEAP到 LK_INIT_LEVEL_KERNEL-1等级的任务运行
  11. 初始化多线程、初始化时钟、初始化write_port_list中的port口
  12. 触发 LK_INIT_LEVEL_KERNEL到 LK_INIT_LEVEL_THREADING -1等级的任务运行
  13. 创建线程 bootstrap2 然后运行
# buildsystemrtoslktopmain.c

void lk_main(ulong arg0, ulong arg1, ulong arg2, ulong arg3)
{
    // save the boot args
    lk_boot_args[0] = arg0;
    lk_boot_args[1] = arg1;
    lk_boot_args[2] = arg2;
    lk_boot_args[3] = arg3;

    // 1. 配置当前线程为bootstrap,优先级为HIGHEST_PRIORITY
    thread_init_early();
    // 2. 触发 LK_INIT_LEVEL_EARLIEST 到 LK_INIT_LEVEL_ARCH_EARLY - 1等级的任务运行
    lk_primary_cpu_init_level(LK_INIT_LEVEL_EARLIEST, LK_INIT_LEVEL_ARCH_EARLY - 1);
    // 3. 切换CPU 从EL3 到 EL1,初始化MMU 
    arch_early_init();
	// 4. 触发 LK_INIT_LEVEL_ARCH_EARLY到 LK_INIT_LEVEL_PLATFORM_EARLY- 1等级的任务运行
    // do any super early platform initialization
    lk_primary_cpu_init_level(LK_INIT_LEVEL_ARCH_EARLY, LK_INIT_LEVEL_PLATFORM_EARLY - 1);
    // 5. 串口初始化、gic中断初始、通过psci_call 启动剩余的CPU
    platform_early_init();

    // 6. 触发 LK_INIT_LEVEL_PLATFORM_EARLY到 LK_INIT_LEVEL_TARGET_EARLY - 1等级的任务运行
    lk_primary_cpu_init_level(LK_INIT_LEVEL_PLATFORM_EARLY, LK_INIT_LEVEL_TARGET_EARLY - 1);
    target_early_init();	//空函数

    dprintf(INFO, "nwelcome to lk/MPnn");
    dprintf(INFO, "boot args 0x%lx 0x%lx 0x%lx 0x%lxn",  lk_boot_args[0], lk_boot_args[1], lk_boot_args[2], lk_boot_args[3]);

    // 7. 触发 LK_INIT_LEVEL_TARGET_EARLY到 LK_INIT_LEVEL_HEAP - 1等级的任务运行
    lk_primary_cpu_init_level(LK_INIT_LEVEL_TARGET_EARLY, LK_INIT_LEVEL_HEAP - 1);
    dprintf(SPEW, "initializing heapn");
    // 8. 初始化堆Heap
    heap_init();
	// 9. 运行 .ctors 段中的函数
    // deal with any static constructors
    dprintf(SPEW, "calling constructorsn");
    call_constructors();
	// 10. 触发 LK_INIT_LEVEL_HEAP到 LK_INIT_LEVEL_KERNEL-1等级的任务运行
    // initialize the kernel
    lk_primary_cpu_init_level(LK_INIT_LEVEL_HEAP, LK_INIT_LEVEL_KERNEL - 1);
    // 11. 初始化多线程、初始化时钟、初始化write_port_list中的port口
    kernel_init();
	// 12. 触发 LK_INIT_LEVEL_KERNEL到 LK_INIT_LEVEL_THREADING -1等级的任务运行
    lk_primary_cpu_init_level(LK_INIT_LEVEL_KERNEL, LK_INIT_LEVEL_THREADING - 1);

	// 13. 创建线程 bootstrap2 然后运行
    // create a thread to complete system initialization
    dprintf(SPEW, "creating bootstrap completion threadn");
    thread_t *t = thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
    thread_set_pinned_cpu(t, 0);
    thread_detach(t);
    thread_resume(t);

    // become the idle thread and enable interrupts to start the scheduler
    thread_become_idle();
}

1.2.1 任务段 .lk_init 分析
# buildsystemrtoslk_bootbuild-preloader_x9_plus_ref_serdeslk.elf.map
.lk_init        0xffff000057a22df8       0xd8 load address 0x0000000057a22df8
                0xffff000057a22df8                __lk_init = .
 *(.lk_init)
 .lk_init       0xffff000057a22df8       0x18 lk_boot/build-preloader_x9_plus_ref_serdes/lk_boot/platform/kunlun/ap1.mod.o
                0xffff000057a22df8                _init_struct_demo
 .lk_init       0xffff000057a22e10       0x48 lk_boot/build-preloader_x9_plus_ref_serdes/lk_boot/chipdev/interrupt/arm_gic.mod.o
                0xffff000057a22e10                _init_struct_arm_gic_resume_cpu
                0xffff000057a22e28                _init_struct_arm_gic_suspend_cpu
                0xffff000057a22e40                _init_struct_arm_gic_init_percpu
 .lk_init       0xffff000057a22e58       0x18 lk_boot/build-preloader_x9_plus_ref_serdes/lk_boot/hal/crypto_hal/.mod.o
                0xffff000057a22e58                _init_struct_cryto_init
 .lk_init       0xffff000057a22e70       0x18 lk_boot/build-preloader_x9_plus_ref_serdes/lk/lib/version.mod.o
                0xffff000057a22e70                _init_struct_version
 .lk_init       0xffff000057a22e88       0x18 lk_boot/build-preloader_x9_plus_ref_serdes/lk_boot/platform/kunlun/common.mod.o
                0xffff000057a22e88                _init_struct_nocache
 .lk_init       0xffff000057a22ea0       0x30 lk_boot/build-preloader_x9_plus_ref_serdes/lk/kernel/vm.mod.o
                0xffff000057a22ea0                _init_struct_vm
                0xffff000057a22eb8                _init_struct_vm_preheap
                0xffff000057a22ed0                __lk_init_end = .

各函数及等级对应如下:

函数入口Level等级文件路径
print_version()LK_INIT_LEVEL_HEAP - 1rtoslklibversionversion.c
vm_init_preheap()LK_INIT_LEVEL_HEAP - 1rtoslkkernelvmvm.c
vm_init_nocache()LK_INIT_LEVEL_VMrtoslk_bootplatformkunluncommonarm64com_platform.c
vm_init_postheap()LK_INIT_LEVEL_VMrtoslkkernelvmvm.c
hal_crypto_init()LK_INIT_LEVEL_TARGETrtoslk_boothalcrypto_halsrccrypto_hal.c
test_entry()LK_INIT_LEVEL_TARGETrtoslk_bootplatformkunlunap1demo.c

这些函数,我大致看了下,对开机流程基本无影响,先跳过吧,有兴趣的兄弟,我给出来路径来,自已看下。


1.3 bootstrap2 线程:解析并执行.app段中的preloader app

主要工作如下 :

  1. 多核 CPU 初始化,释放除CPU0 外的其他CPU,因为刚刚在 bootstrap 中启动了。
  2. sd_display_create 显示初始化、DMA初始化
# buildsystemrtoslktopmain.c
static int bootstrap2(void *arg)
{
    dprintf(SPEW, "top of bootstrap2()n");

    lk_primary_cpu_init_level(LK_INIT_LEVEL_THREADING, LK_INIT_LEVEL_ARCH - 1);
    // 1. 多核 CPU 初始化,释放除CPU0 外的其他CPU,因为刚刚在 bootstrap 中启动了。
    arch_init();

    dprintf(SPEW, "initializing platformn");
    lk_primary_cpu_init_level(LK_INIT_LEVEL_ARCH, LK_INIT_LEVEL_PLATFORM - 1);
    // 2. sd_display_create 显示初始化、DMA初始化
    platform_init();
   
    // initialize the target
    dprintf(SPEW, "initializing targetn");
    lk_primary_cpu_init_level(LK_INIT_LEVEL_PLATFORM, LK_INIT_LEVEL_TARGET - 1);
    target_init();	// 空函数

    dprintf(SPEW, "calling apps_init()n");
    lk_primary_cpu_init_level(LK_INIT_LEVEL_TARGET, LK_INIT_LEVEL_APPS - 1);
    // 3. 运行 .app 段的所有函数
    apps_init();
    lk_primary_cpu_init_level(LK_INIT_LEVEL_APPS, LK_INIT_LEVEL_LAST);

    return 0;
}

1.3.1 应用段 .apps 分析

从map 文件中,可以看出,有 shell 和 preloader 两个app
启动app时,会先调用 .init,然后调用 .entry方法

# rtoslk_bootbuild-preloader_x9_plus_ref_serdeslk.elf.map
.apps           0xffff000057a22da8       0x50 load address 0x0000000057a22da8
                0xffff000057a22da8                __apps_start = .
 *(.apps)
 .apps          0xffff000057a22da8       0x28 lk_boot/build-preloader_x9_plus_ref_serdes/lk/app/shell.mod.o
                0xffff000057a22da8                _app_shell
 .apps          0xffff000057a22dd0       0x28 lk_boot/build-preloader_x9_plus_ref_serdes/lk_boot/application/system/preloader.mod.o
                0xffff000057a22dd0                _app_preloader
                0xffff000057a22df8                __apps_end = .

1.3.2 启动shell app
# rtoslkappshellshell.c
static void shell_init(const struct app_descriptor *app){
	// 在init中,会遍历注册所有保存在 .command段中的命令
    console_init();
    ----------------->
    +	cmd_block *block;
	+   for (block = __commands_start; block != __commands_end; block++) {
	+       console_register_commands(block);
	+   }
    <-----------------
}
static void shell_entry(const struct app_descriptor *app, void *args){
	// 在shell 中不停的读取输入命令并检测执行
    console_start();
    ---------------->
    +	while (command_loop(&read_debug_line, NULL, true, false) == NO_ERROR);
    <----------------
}
APP_START(shell)
.init = shell_init,
.entry = shell_entry,
APP_END

1.3.2.1 shell app中支持的所有命令
# rtoslk_bootbuild-preloader_x9_plus_ref_serdeslk.elf.map
.commands       0xffff000057a23fe8       0xf0 load address 0x0000000057a23fe8
                0xffff000057a23fe8                __commands_start = .
 *(.commands)
 .commands      0xffff000057a23fe8       0x18 lk_boot/build-preloader_x9_plus_ref_serdes/lk_boot/application/system/preloader.mod.o
                0xffff000057a23fe8                _cmd_block_preloader
 .commands      0xffff000057a24000       0x18 lk_boot/build-preloader_x9_plus_ref_serdes/lk_boot/chipdev/fuse_ctrl.mod.o
                0xffff000057a24000                _cmd_block_fuse
 .commands      0xffff000057a24018       0x18 lk_boot/build-preloader_x9_plus_ref_serdes/lk/kernel.mod.o
                0xffff000057a24018                _cmd_block_kernel
 .commands      0xffff000057a24030       0x18 lk_boot/build-preloader_x9_plus_ref_serdes/lk/lib/version.mod.o
                0xffff000057a24030                _cmd_block_version
 .commands      0xffff000057a24048       0x18 lk_boot/build-preloader_x9_plus_ref_serdes/lk/platform.mod.o
                0xffff000057a24048                _cmd_block_platform_power
 .commands      0xffff000057a24060       0x48 lk_boot/build-preloader_x9_plus_ref_serdes/lk/kernel/vm.mod.o
                0xffff000057a24060                _cmd_block_pmm
                0xffff000057a24078                _cmd_block_vm
                0xffff000057a24090                _cmd_block_vmm
 .commands      0xffff000057a240a8       0x18 lk_boot/build-preloader_x9_plus_ref_serdes/lk/lib/console.mod.o
                0xffff000057a240a8                _cmd_block_help
 .commands      0xffff000057a240c0       0x18 lk_boot/build-preloader_x9_plus_ref_serdes/lk/external/lib/cksum.mod.o
                0xffff000057a240c0                _cmd_block_crc
                0xffff000057a240d8                __commands_end = .

可以看出,在 preloade shell中,支持这些命令:
preloader、fuse、kernel、version、platform_power、pmm、vm、vmm、help、crc


1.3.3 启动preloader app:
# rtoslk_bootapplicationsystempreloaderpreloader.c

APP_START(preloader)
.flags = 0,
.entry = preloader_entry,
APP_END

可以看到 preloader app 的入口函数为 preloader_entry,主要工作如下:

  1. 解析boot pin引脚配置,根据 boot pin可以决定启动的设备
  2. 初始化启动设备,同时读取table信息
  3. 开启 SUPPORT_STORE_EXCEPTION_LOG 或 SUPPORT_CRASHDUMP_FEATURE 则能看到,当flash引发的错误时的log
  4. 加载分区表
  5. 初始化 boot_args 链表
  6. 配置preloader_configs中要加载镜像的条件触发函数prepare 和complete
  7. 正式加载镜像
# rtoslk_bootapplicationsystempreloaderpreloader.c
static void preloader_entry(const struct app_descriptor *app, void *args)
{
    partition_device_t *ptdev  = NULL;
    storage_device_t *storage  = NULL;
    boot_device_cfg_t *btdev_cfg = NULL;
    // 1. 解析boot pin引脚配置,根据 boot pin可以决定启动的设备
	uint32_t pin = boot_get_pin();
    int ret = 0;
    dprintf(INFO, "%s!n", __func__);
    if (BOOT_PIN_0 == pin) {
        uint32_t pin_ap = boot_get_pin_scr_overwrite();
        if (pin_ap != PIN_ERROR) {
            pin = pin_ap;
        }
    }
    btdev_pin_cfg_t *btdev_pin = find_btdev(pin);
    KEY_NODE_INFO("preloader start!n");
    if (btdev_pin) {
        btdev_cfg =  btdev_pin->ap;
    }
    if (btdev_cfg) {
        storage = setup_storage_dev(btdev_cfg->device_type, btdev_cfg->res_idex, (void *)&btdev_cfg->cfg);
    }
	
	// 2. 初始化启动设备,同时读取table信息
    ASSERT(storage != NULL);
    ptdev = ptdev_setup(storage, BOOT_DEVICE_GPT_START);
    ASSERT(ptdev != NULL);
    ptdev_read_table(ptdev);

	// 3. 开启 SUPPORT_STORE_EXCEPTION_LOG 或 SUPPORT_CRASHDUMP_FEATURE 则能看到,当flash引发的错误时的log
#if (SUPPORT_STORE_EXCEPTION_LOG || SUPPORT_CRASHDUMP_FEATURE)
    
    sdrv_exp_rst_cnt_set(FLAG_ID_SAF, 0);

    
    if (sdrv_ap_exp_flag_get()) {
        sdrv_ap_exp_flag_set(0);
#if SUPPORT_CRASHDUMP_FEATURE
        if(sdrv_crashdump_flag_get()) {
            dprintf(ALWAYS, "in crashdump mode!!!n");
            if (CPU_ID_CURRENT == CPU_ID_AP1)
                sdrv_crashdump_enter(CPU_ID_AP1);
            else if (CPU_ID_CURRENT == CPU_ID_AP2)
                sdrv_crashdump_enter(CPU_ID_AP2);
            else
                dprintf(ALWAYS, "Not support enter crashdump mode!!!n");
        }
#endif
#if SUPPORT_STORE_EXCEPTION_LOG
       if (CPU_ID_CURRENT == CPU_ID_AP1)
           ret = sdrv_exp_log_store(ptdev, AP1_A55, VIRT_UART_MEMBASE, VIRT_UART_MEMSIZE + 0x100000);
       else
           ret = sdrv_exp_log_store(ptdev, AP2_A55, VIRT_UART_MEMBASE, VIRT_UART_MEMSIZE + 0x100000);

        
        sdrv_global_exp_flag_set(1);

        sdrv_exp_rst_cnt_add(FLAG_ID_AP1, 1);
        if (sdrv_is_loop_exp_restart(FLAG_ID_AP1)) {
            dprintf(ALWAYS, "system may be in ap exception loop restart, stop !!!n");
            return;
        }
#endif
    }
#endif

    
    if (BOOT_PIN_0 == boot_get_pin()) {
        ptdev_roll_back_check(ptdev);
    }
    // 4. 加载分区表
#if VERIFIED_BOOT
    load_vbmeta_partition(ptdev);
#endif
	// 5. 初始化 boot_args 链表
    boot_args_init();
    high_backup_init();
    // 6. PCIE 初始化
#if SUPPORT_PCIE_LOAD
    pcie_rc_config();
#endif
	// 7. 配置preloader_configs中要加载镜像的条件触发函数prepare 和complete
    set_default_action((void *)ptdev);
    =============>
    +	register_load_prepare_for_all(preloader_configs, PRELOADER_CONFIGS_COUNT, default_prepare, arg);
    +	register_load_complete_for_all(preloader_configs, PRELOADER_CONFIGS_COUNT, default_complete, arg);
    <============
    // 8. 正式加载镜像
    ret = load_all_partition(preloader_configs, PRELOADER_CONFIGS_COUNT, ptdev);
}

我们来看看 load_all_partition() 中干了啥:
在 load_all_partition() 中主要是循环解析 preloader_configs 中的分区镜像,
然后镜像对应的prepare() 函数,加载好镜像后,调用complete()函数

# rtoslk_bootincludepartition_load_configs.h
static int load_all_partition(struct pt_load_config *configs, uint32_t count, partition_device_t *ptdev)
{
    storage_device_t *storage     = NULL;
    const char *ptname            = NULL;
    unsigned long long ptn        = 0;
    unsigned long long pt_size    = 0;
    vaddr_t load_addr             = 0;
    struct pt_load_config *config = NULL;
    uint32_t switch_pt_num        = BOOT_DEVICE_USER_DATA;
    AvbFooter footer = {0};
    uint32_t bk_sz;
    uint8_t * block_buf = NULL;

    storage = ptdev->storage;

    if (storage->switch_part)
        storage->switch_part(storage, switch_pt_num);

    bk_sz = storage->get_block_size(storage);
    block_buf = memalign(bk_sz, bk_sz);
    config = configs;

    for (uint32_t i = 0; i < count; i++, config++) {
        ptname   = config->pt_name;
        ptn      = ptdev_get_offset(ptdev, ptname);
        pt_size  = ptdev_get_size(ptdev, ptname);

        if (block_buf && !storage->read(storage, ptn + pt_size - bk_sz, block_buf, bk_sz))
        {
            if (avb_footer_validate_and_byteswap((AvbFooter *)(block_buf+bk_sz - sizeof(AvbFooter)), &footer))
                    pt_size = footer.original_image_size;
        }


        
        config->load_size = pt_size;
        if (config->prepare && config->prepare((void*)config, config->pre_arg)) {
            dprintf(CRITICAL, "prepare to load partition error. name:%sn", ptname);
            continue;
        }

        
        if (pt_size > config->load_size)
            panic("Oops:image size is larger than buffer. name:%sn", ptname);

        if (config->flags & PT_AP2SEC_F) {
            load_addr = (vaddr_t)_ioaddr(ap2p(config->load_addr));
        }
        else {
            load_addr = (vaddr_t)_ioaddr(config->load_addr);
        }

        if (storage->read(storage, ptn, (uint8_t *)load_addr, round_up(pt_size, bk_sz))){
            panic("load paritition error.name:%s n", ptname);
        }

        config->load_complete = true;
        if (config->complete && config->complete((void*)config,configs->post_arg)) {
            panic("complete load partition error.name:%sn", ptname);
        }
    }

    if (block_buf)
        free(block_buf);

    return 0;
}


1.3.4 preloader_configs 解析

preloader_configs 定义如下:

# rtoslk_bootincludepartition_load_configs.h
#define PT_LOAD_CONFIG_ITEM(pt_type, order, addr, size, cpu, flag, name)   
    {                                                           
        .type = pt_type,                                        
        .load_order = order,                                    
        .load_complete = false,                                 
        .load_addr = addr,                                      
        .load_size = size,                                      
        .cpu_id = cpu,                                          
        .flags = flag,                                          
        .pt_name = {#name},                                     
        .prepare = NULL,                                        
        .complete = NULL,                                       
        .pre_arg  = NULL,                                       
        .post_arg = NULL,                                       
        .last_config = false,                                   
    },                                                          
#define PT_LOAD_CONFIGS_END  };


# rtoslk_bootchipcfggeneratex9_high-plusprojectsserdespreloader_configs.h
#define CPU_ID_CURRENT CPU_ID_AP1
#define CONFIGS 
            PT_LOAD_CONFIG_ITEM(PT_BL,  0, AP2_KERNEL_MEMBASE, AP2_KERNEL_MEMSIZE, CPU_ID_AP2, PT_BAK_HIGH_F, cluster_kernel) 
            PT_LOAD_CONFIG_ITEM(PT_BL,  1, AP2_REE_MEMBASE+BOARD_RAMDISK_OFFSET, AP2_BOARD_RAMDISK_MEMSIZE, CPU_ID_AP2, PT_BAK_HIGH_F, cluster_ramdisk) 
            PT_LOAD_CONFIG_ITEM(PT_BL,  2, AP2_REE_MEMBASE, BOARD_KERNEL_OFFSET, CPU_ID_AP2, PT_BAK_HIGH_F, cluster_dtb) 
            PT_LOAD_CONFIG_ITEM(PT_BL,  3, AP2_BOOTLOADER_MEMBASE, AP2_BOOTLOADER_MEMSIZE, CPU_ID_AP2, PT_BAK_HIGH_F, cluster_bootloader) 
            PT_LOAD_CONFIG_ITEM(PT_SML, 4, AP2_ATF_MEMBASE, AP2_ATF_MEMSIZE, CPU_ID_AP2, PT_BAK_HIGH_F, cluster_atf) 
            PT_LOAD_CONFIG_ITEM(PT_TOS, 5, AP2_TEE_MEMBASE, AP2_TEE_MEMSIZE, CPU_ID_AP2, PT_BAK_HIGH_F, cluster_tos) 
            PT_LOAD_CONFIG_ITEM(PT_BL,  6, AP2_BOOTLOADER_MEMBASE, AP2_BOOTLOADER_MEMSIZE, CPU_ID_AP2, PT_KICK_F|PT_BAK_LOW_F, cluster_preloader) 
            PT_LOAD_CONFIG_ITEM(PT_SML, 7, AP1_ATF_MEMBASE, AP1_ATF_MEMSIZE, CPU_ID_AP1, PT_SAVE_F, atf) 
            PT_LOAD_CONFIG_ITEM(PT_TOS, 8, AP1_TEE_MEMBASE, AP1_TEE_MEMSIZE, CPU_ID_AP1, PT_SAVE_F, tos) 
            PT_LOAD_CONFIG_ITEM(PT_BL,  9, AP1_BOOTLOADER_MEMBASE, AP1_BOOTLOADER_MEMSIZE, CPU_ID_AP1, PT_KICK_F|PT_SAVE_F, bootloader) 

#define CONFIGS_AP2 
            PT_LOAD_CONFIG_ITEM(PT_BL,  0, AP2_KERNEL_MEMBASE, AP2_KERNEL_MEMSIZE, CPU_ID_AP2, 0, cluster_kernel) 
            PT_LOAD_CONFIG_ITEM(PT_BL,  1, AP2_REE_MEMBASE+BOARD_RAMDISK_OFFSET, AP2_BOARD_RAMDISK_MEMSIZE, CPU_ID_AP2, 0, cluster_ramdisk) 
            PT_LOAD_CONFIG_ITEM(PT_BL,  2, AP2_REE_MEMBASE, BOARD_KERNEL_OFFSET, CPU_ID_AP2, 0, cluster_dtb) 
            PT_LOAD_CONFIG_ITEM(PT_SML, 3, AP2_ATF_MEMBASE, AP2_ATF_MEMSIZE, CPU_ID_AP2, 0, cluster_atf) 
            PT_LOAD_CONFIG_ITEM(PT_TOS, 4, AP2_TEE_MEMBASE, AP2_TEE_MEMSIZE, CPU_ID_AP2, 0, cluster_tos) 
            PT_LOAD_CONFIG_ITEM(PT_BL,  5, AP2_BOOTLOADER_MEMBASE, AP2_BOOTLOADER_MEMSIZE, CPU_ID_AP2, PT_KICK_F, cluster_bootloader) 
#endif

1.3.5 AP1 启动镜像加载:启动 bootloader

可以看出,在 启动 AP1 时分别会加载如下镜像:

  1. cluster_kernel
  2. cluster_ramdisk
  3. cluster_dtb
  4. cluster_bootloader
  5. cluster_atf
  6. cluster_tos
  7. cluster_preloader
  8. atf
  9. tos
  10. bootloader
# rtoslk_bootchipcfggeneratex9_high-plusprojectsserdespreloader_configs.h
#define CPU_ID_CURRENT CPU_ID_AP1
#define CONFIGS 
            PT_LOAD_CONFIG_ITEM(PT_BL,  0, AP2_KERNEL_MEMBASE, AP2_KERNEL_MEMSIZE, CPU_ID_AP2, PT_BAK_HIGH_F, cluster_kernel) 
            PT_LOAD_CONFIG_ITEM(PT_BL,  1, AP2_REE_MEMBASE+BOARD_RAMDISK_OFFSET, AP2_BOARD_RAMDISK_MEMSIZE, CPU_ID_AP2, PT_BAK_HIGH_F, cluster_ramdisk) 
            PT_LOAD_CONFIG_ITEM(PT_BL,  2, AP2_REE_MEMBASE, BOARD_KERNEL_OFFSET, CPU_ID_AP2, PT_BAK_HIGH_F, cluster_dtb) 
            PT_LOAD_CONFIG_ITEM(PT_BL,  3, AP2_BOOTLOADER_MEMBASE, AP2_BOOTLOADER_MEMSIZE, CPU_ID_AP2, PT_BAK_HIGH_F, cluster_bootloader) 
            PT_LOAD_CONFIG_ITEM(PT_SML, 4, AP2_ATF_MEMBASE, AP2_ATF_MEMSIZE, CPU_ID_AP2, PT_BAK_HIGH_F, cluster_atf) 
            PT_LOAD_CONFIG_ITEM(PT_TOS, 5, AP2_TEE_MEMBASE, AP2_TEE_MEMSIZE, CPU_ID_AP2, PT_BAK_HIGH_F, cluster_tos) 
            PT_LOAD_CONFIG_ITEM(PT_BL,  6, AP2_BOOTLOADER_MEMBASE, AP2_BOOTLOADER_MEMSIZE, CPU_ID_AP2, PT_KICK_F|PT_BAK_LOW_F, cluster_preloader) 
            PT_LOAD_CONFIG_ITEM(PT_SML, 7, AP1_ATF_MEMBASE, AP1_ATF_MEMSIZE, CPU_ID_AP1, PT_SAVE_F, atf) 
            PT_LOAD_CONFIG_ITEM(PT_TOS, 8, AP1_TEE_MEMBASE, AP1_TEE_MEMSIZE, CPU_ID_AP1, PT_SAVE_F, tos) 
            PT_LOAD_CONFIG_ITEM(PT_BL,  9, AP1_BOOTLOADER_MEMBASE, AP1_BOOTLOADER_MEMSIZE, CPU_ID_AP1, PT_KICK_F|PT_SAVE_F, bootloader) 

依次遍历上面的镜像,这些镜像触发函数为default_complete():

static int default_complete(void *config, void *arg)
{
    sd_cpu_id cpu_id;
    struct pt_load_config *_config = config;
    cpu_id = _config->cpu_id;
	if (_config->flags & PT_SAVE_F) {
        push_boot_arg(_ioaddr(_config->load_addr), _config->load_size, _config->pt_name, _config->cpu_id);
    }

    if (cpu_id == CPU_ID_CURRENT&& (_config->flags & PT_KICK_F)) {
        count = boot_args_count(cpu_id);
        partition_device_t *ptdev = arg;
		verify_loaded_images(ptdev, get_head(cpu_id), NULL)

        if (count == 1) {
            entry = pop_boot_addr(cpu_id);
            dprintf(CRITICAL, "ENTRY:0x%llxn", entry);
            
            arch_chain_load((void *)entry, 0, 0, 0, 0);
        }
        else if (count == 2) {
            arg3 = v2p((void *)pop_boot_addr(cpu_id));
            arg1 = v2p((void *)pop_boot_addr(cpu_id));
        }
        else if (count == 3) {
            arg3 = v2p((void *)pop_boot_addr(cpu_id));
            arg2 = v2p((void *)pop_boot_addr(cpu_id));
            arg1 = v2p((void *)pop_boot_addr(cpu_id));
        }

        dprintf(CRITICAL, "%s %d arg1:0x%0llx arg2:0x%0llx arg3:0x%0llxn",
                __func__, __LINE__, arg1, arg2, arg3);
        arch_disable_ints();
        arch_disable_cache(UCACHE);
        KEY_NODE_INFO("preloader call smc!n");
        smc(SMC_RUN_IMAGE_BL31, arg1, arg2, arg3, 0, 0, 0, 0);
    }
    return 0;
}

可以看出,判断条件为 cpu_id == CPU_ID_CURRENT 且 _config->flags*PT_KICK_F,
我们现在跑的是AP1,结合前面的镜像列表,可以得出,
启动 AP1时:
atf、tos 这两个镜像在加载完成后,由于其flag为PT_SAVE_F,加载后会将这两个镜像放入boot_args中。
bootloader 则在加载后,由于其flag为PT_KICK_F,则会配置 arg1=atf的entry, arg3 = tos的entry
接下来,调smc 指令,触发一个异常后,至此 android preloader 生命周期结束。


1.3.6 smc跳转进入 BL31环境
# buildsystemrtoslkarcharm64asm.S
FUNCTION (smc)
    smc #0
    ret

意思就是,此时smc指令触发异常后,会进入 SMC_RUN_IMAGE_BL31 环境,传参分别为 arg1=atf, arg3 = tos

至此 android preloader 生命周期结束, 本文先分析到这,下文我们接着分析。





转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/972829.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号