MTK bootloader 启动过程

1、bootloader到kernel启动总逻辑流程图

ARM架构中,EL0/EL1是必须实现,EL2/EL3是选配,ELx跟层级对应关系:

EL0 -- app

EL1 -- Linux kernel 、lk

EL2 -- hypervisor(虚拟化)

EL3 -- ARM trust firmware 、pre-loader

若平台未实现EL3(atf),pre-loader直接加载lk:

 

若平台实现EL3,则需要先加载完ATF再由ATF去加载lk:

bootloader 启动分两个阶段,一个是pre-loader加载lk(u-boot)阶段,另一个是lk加载kernel阶段。下面跟着流程图简述第一个阶段的加载流程。

1-3:设备上电起来后,跳转到Boot ROM(不是flash)中的boot code中执行把pre-loader加载起到ISRAM, 因为当前DRAM(RAM分SRAM跟DRAM,简单来说SRAM就是cache,DRAM就是普通内存)还没有准备好,所以要先把pre-loader load到芯片内部的ISRAM(Internal SRAM)中。

4-6:pre-loader初始化好DRAM后就将lk从flash(nand/emmc)中加载到DRAM中运行;

7-8:解压bootimage成ramdisk跟kernel并载入DRAM中,初始化dtb;

9-11:lk跳转到kernl初始化, kernel初始化完成后fork出init进程, 然后拉起ramdisk中的init程序,进入用户空间初始化,init进程fork出zygote进程..直到整个Android启动完成.

 

2、从pre-loader到lk(mt6580为例)

Pre-loader主要干的事情就是初始化某些硬件,比如: UART,GPIO,DRAM,TIMER,RTC,PMIC 等等,建立起最基本的运行环境,最重要的就是初始化DRAM.

时序图:

点击查看大图

源码流程如下:

 
  1. ./bootloader/preloader/platform/mt6580/src/init/init.s

  2.  
  3. .section .text.start

  4. ...

  5.  
  6. .globl _start

  7. ...

  8.  
  9. /* set the cpu to SVC32 mode */

  10. MRS r0,cpsr

  11. BIC r0,r0,#0x1f

  12. ORR r0,r0,#0xd3

  13. MSR cpsr,r0

  14.  
  15. /* disable interrupt */

  16. MRS r0, cpsr

  17. MOV r1, #INT_BIT

  18. ORR r0, r0, r1

  19. MSR cpsr_cxsf, r0

  20.  
  21. ...

  22. setup_stk :

  23. /* setup stack */

  24. LDR r0, stack

  25. LDR r1, stacksz

  26. ...

  27.  
  28. entry :

  29. LDR r0, =bldr_args_addr

  30.  
  31. /* 跳转到C代码 main 入口 */

  32. B main

init.s 主要干的事情是切换系统到管理模式(svc)(如果平台有实现el3,那么pre-loader运行在el3,否则运行在el1),禁止irq/fiq,设置stack等, 然后jump到c代码main函数入口。 

进入源码分析。

 
  1. ./bootloader/preloader/platform/mt6580/src/core/main.c

  2.  
  3. void main(u32 *arg)

  4. {

  5. struct bldr_command_handler handler;

  6. u32 jump_addr, jump_arg;

  7.  
  8. /* get the bldr argument */

  9. bldr_param = (bl_param_t *)*arg;

  10.  
  11. // 初始化uart

  12. mtk_uart_init(UART_SRC_CLK_FRQ, CFG_LOG_BAUDRATE);

  13.  
  14. // 这里干了很多事情,包括各种的平台硬件(timer,pmic,gpio,wdt...)初始化工作.

  15. bldr_pre_process();

  16.  
  17. handler.priv = NULL;

  18. handler.attr = 0;

  19. handler.cb = bldr_cmd_handler;

  20.  
  21. // 这里是获取启动模式等信息保存到全局变量g_boot_mode和g_meta_com_type 中.

  22. BOOTING_TIME_PROFILING_LOG("before bldr_handshake");

  23. bldr_handshake(&handler);

  24. BOOTING_TIME_PROFILING_LOG("bldr_handshake");

  25.  
  26. // 下面跟 secro img 相关,跟平台设计强相关.

  27. /* security check */

  28. sec_lib_read_secro();

  29. sec_boot_check();

  30. device_APC_dom_setup();

  31.  
  32. BOOTING_TIME_PROFILING_LOG("sec_boot_check");

  33.  
  34. /* 如果已经实现EL3,那么进行tz预初始化 */

  35. #if CFG_ATF_SUPPORT

  36. trustzone_pre_init();

  37. #endif

  38.  
  39. /* bldr_load_images

  40. 此函数要做的事情就是把lk从ROM中指定位置load到DRAM中,开机log中可以看到具体信息:

  41. [PART] load "lk" from 0x0000000001CC0200 (dev) to 0x81E00000 (mem) [SUCCESS]

  42. 这里准备好了jump到DRAM的具体地址,下面详细分析.

  43. */

  44. if (0 != bldr_load_images(&jump_addr)) {

  45. print("%s Second Bootloader Load Failed\n", MOD);

  46. goto error;

  47. }

  48.  
  49. /*

  50. 该函数的实现体是platform_post_init,这里要干的事情其实比较简单,就是通过

  51. hw_check_battery去判断当前系统是否存在电池(判断是否有电池ntc脚来区分),

  52. 如果不存在就陷入while(1)卡住了,所以在es阶段调试有时候

  53. 需要接电源调试的,就需要改这里面的逻辑才可正常开机

  54. */

  55. bldr_post_process();

  56.  
  57. // atf 正式初始化,使用特有的系统调用方式实现.

  58. #if CFG_ATF_SUPPORT

  59. trustzone_post_init();

  60. #endif

  61.  
  62. /* 跳转传入lk的参数,包括boot time/mode/reason 等,这些参数在

  63. platform_set_boot_args 函数获取。

  64. */

  65. jump_arg = (u32)&(g_dram_buf->boottag);

  66.  
  67.  
  68. /* 执行jump系统调用,从 pre-loader 跳转到 lk执行,

如果实现了EL3情况就要复杂一些,需要先跳转到EL3初始化,然后再跳回lk,pre-loader执行在EL3,LK执行在EL1)

从log可以类似看到这些信息: [BLDR] jump to 0x81E00000 [BLDR] <0x81E00000>=0xEA000007 [BLDR] <0x81E00004>=0xEA0056E2 */ #if CFG_ATF_SUPPORT /* 64S3,32S1,32S1 (MTK_ATF_BOOT_OPTION = 0) * re-loader jump to LK directly and then LK jump to kernel directly */ if ( BOOT_OPT_64S3 == g_smc_boot_opt && BOOT_OPT_32S1 == g_lk_boot_opt && BOOT_OPT_32S1 == g_kernel_boot_opt) { print("%s 64S3,32S1,32S1, jump to LK\n", MOD); bldr_jump(jump_addr, jump_arg, sizeof(boot_arg_t)); } else {// 如果 el3 使用aarch64实现,则jump到atf. print("%s Others, jump to ATF\n", MOD); bldr_jump64(jump_addr, jump_arg, sizeof(boot_arg_t)); } #else bldr_jump(jump_addr, jump_arg, sizeof(boot_arg_t)); #endif // 如果没有取到jump_addr,则打印错误提示,进入while(1)等待. error: platform_error_handler(); }

main 函数小结:

1、各种硬件初始化(uart、pmic、wdt、timer、mem..);

2、获取系统启动模式等,保存在全局变量中;

3、Security check,跟secro.img相关;

4、如果系统已经实现el3,则进入tz初始化;

5、获取lk加载到DRAM的地址(固定值),然后从ROM中找到lk分区的地址, 如果没找到jump_addr,则 goto error;

6、battery check,如果没有电池就会陷入while(1);

7、jump到lk(如果有实现el3,则会先jump到el3,然后再回到lk)

 

3、重点函数分析

bldr_load_images

函数主要干的事情就是找到lk分区地址和lk加载到DRAM中的地址, 准备好jump到lk执行,如下源码分析:

 
  1. static int bldr_load_images(u32 *jump_addr)

  2. {

  3. int ret = 0;

  4. blkdev_t *bootdev;

  5. u32 addr = 0;

  6. char *name;

  7. u32 size = 0;

  8. u32 spare0 = 0;

  9. u32 spare1 = 0;

  10.  
  11. ...

  12. /* 这个地址是一个固定值,可以查到定义在:

  13. ./bootloader/preloader/platform/mt6580/default.mak:95:

  14. CFG_UBOOT_MEMADDR := 0x81E00000

  15. 从log中可以看到:

  16. [BLDR] jump to 0x81E00000

  17. */

  18. addr = CFG_UBOOT_MEMADDR;

  19.  
  20. /* 然后去ROM找到lk所在分区地址 */

  21. ret = bldr_load_part("lk", bootdev, &addr, &size);

  22. if (ret)

  23. return ret;

  24. *jump_addr = addr;

  25.  
  26. }

  27.  
  28. // 这个函数逻辑很简单,就不需要多说了.

  29. int bldr_load_part(char *name, blkdev_t *bdev, u32 *addr, u32 *size)

  30. {

  31. part_t *part = part_get(name);

  32.  
  33. if (NULL == part) {

  34. print("%s %s partition not found\n", MOD, name);

  35. return -1;

  36. }

  37.  
  38. return part_load(bdev, part, addr, 0, size);

  39. }

  40.  
  41. // 真正的load实现是在part_load函数.

  42. int part_load(blkdev_t *bdev, part_t *part, u32 *addr, u32 offset, u32 *size)

  43. {

  44. int ret;

  45. img_hdr_t *hdr = (img_hdr_t *)img_hdr_buf;

  46. part_hdr_t *part_hdr = &hdr->part_hdr;

  47. gfh_file_info_t *file_info_hdr = &hdr->file_info_hdr;

  48.  
  49. /* specify the read offset */

  50. u64 src = part->startblk * bdev->blksz + offset;

  51. u32 dsize = 0, maddr = 0;

  52. u32 ms;

  53.  
  54. // 检索分区头是否正确。

  55. /* retrieve partition header. */

  56. if (blkdev_read(bdev, src, sizeof(img_hdr_t), (u8*)hdr,0) != 0) {

  57. print("[%s]bdev(%d) read error (%s)\n", MOD, bdev->type, part->name);

  58. return -1;

  59. }

  60.  
  61. if (part_hdr->info.magic == PART_MAGIC) {

  62.  
  63. /* load image with partition header */

  64. part_hdr->info.name[31] = '\0';

  65.  
  66. /*

  67. 输出分区的各种信息,从log中可以看到:

  68. [PART] Image with part header

  69. [PART] name : lk

  70. [PART] addr : FFFFFFFFh mode : -1

  71. [PART] size : 337116

  72. [PART] magic: 58881688h

  73. */

  74. print("[%s]Img with part header\n", MOD);

  75. print("[%s]name:%s\n", MOD, part_hdr->info.name);

  76. print("[%s]addr:%xh\n", MOD, part_hdr->info.maddr);

  77. print("[%s]size:%d\n", MOD, part_hdr->info.dsize);

  78. print("[%s]magic:%xh\n", MOD, part_hdr->info.magic);

  79.  
  80. maddr = part_hdr->info.maddr;

  81. dsize = part_hdr->info.dsize;

  82. src += sizeof(part_hdr_t);

  83.  
  84. memcpy(part_info + part_num, part_hdr, sizeof(part_hdr_t));

  85. part_num++;

  86. } else {

  87. print("[%s]%s img not exist\n", MOD, part->name);

  88. return -1;

  89. }

  90.  
  91. // 如果maddr没有定义,那么就使用前面传入的地址addr.

  92. if (maddr == PART_HEADER_MEMADDR/*0xffffffff*/)

  93. maddr = *addr;

  94.  
  95. if_overlap_with_dram_buffer((u32)maddr, ((u32)maddr + dsize));

  96.  
  97. ms = get_timer(0);

  98. if (0 == (ret = blkdev_read(bdev, src, dsize, (u8*)maddr,0)))

  99. *addr = maddr;

  100. ms = get_timer(ms);

  101.  
  102. /* 如果一切顺利就会打印出关键信息:

  103. [PART] load "lk" from 0x0000000001CC0200 (dev) to 0x81E00000 (mem) [SUCCESS]

  104. [PART] load speed: 25324KB/s, 337116 bytes, 13ms

  105. */

  106. print("\n[%s]load \"%s\" from 0x%llx(dev) to 0x%x (mem) [%s]\n", MOD,

  107. part->name, src, maddr, (ret == 0) ? "SUCCESS" : "FAILED");

  108.  
  109. if( ms == 0 )

  110. ms+=1;

  111.  
  112. print("[%s]load speed:%dKB/s,%d bytes,%dms\n", MOD, ((dsize / ms) * 1000) / 1024, dsize, ms);

  113.  
  114.  
  115. return ret;

  116. }

bldr_post_process

函数主要干的事情就是从pmic去检查是否有电池存在,如果没有就等待, 如下源码分析,比较简单:

 
  1. // 就是包了一层而已.

  2. static void bldr_post_process(void)

  3. {

  4. platform_post_init();

  5. }

  6.  
  7. // 重点是这个函数:

  8. void platform_post_init(void)

  9. {

  10. /* normal boot to check battery exists or not */

  11. if (g_boot_mode == NORMAL_BOOT && !hw_check_battery() && usb_accessory_in()) {

  12. ...

  13. pl_charging(1);

  14. do {

  15. mdelay(300);

  16.  
  17. /* 检查电池是否存在, 如果使用电源调试则需要修改此函数逻辑 */

  18. if (hw_check_battery())

  19. break;

  20. /* 喂狗,以免超时被狗咬 */

  21. platform_wdt_all_kick();

  22. } while(1);

  23. /* disable force charging mode */

  24. pl_charging(0);

  25. }

  26.  
  27. ...

  28. }

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MTK(MediaTek)平台上的Bootloader是一个引导加载程序,负责在设备启动时加载操作系统内核。下面是关于MTK平台Bootloader的介绍: 1. 功能:MTK平台的Bootloader主要有以下功能: - 初始化硬件:Bootloader负责初始化设备上的各种硬件组件,如处理器、内存、外设等。 - 加载内核:Bootloader从存储设备(如eMMC、NAND Flash)中读取操作系统内核,并将其加载到内存中。 - 设置内存映射:Bootloader负责设置内存映射表,将内核和其他重要的系统组件映射到正确的内存地址。 - 启动内核:一旦内核加载完成,并且内存映射设置完毕,Bootloader会将控制权交给内核,使其开始执行。 2. 引导过程:MTK平台的Bootloader引导过程一般包括以下几个步骤: - 上电初始化:当设备上电时,Bootloader会执行一系列初始化操作,包括对硬件进行初始化、检测外部设备的连接状态等。 - Bootloader加载:Bootloader自身会从特定的存储设备中加载,通常是从Flash存储器中读取,以确保其可靠性。 - 引导加载程序(Secondary Bootloader):引导加载程序是Bootloader的一部分,负责进一步加载操作系统内核和其他系统组件。 - 内核启动:引导加载程序完成其任务后,会将控制权交给操作系统内核,使其开始执行。内核启动过程涉及初始化和加载驱动程序、启动系统服务等。 3. 可定制性:MTK平台上的Bootloader通常可以进行定制和扩展。设备制造商和开发者可以根据需求对Bootloader进行修改,添加特定的功能或增加自定义的引导流程。 总之,MTK平台上的Bootloader是一个引导加载程序,负责初始化硬件、加载内核、设置内存映射并启动操作系统。它在设备启动时起到关键的引导作用,并在一定程度上可定制和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值