1.前言

本文档主要对MSM8996的启动流程进行一个简要的分析,目的在于展现启动流程的概貌,不会对每个细节做很详细的表述,但会对启动流程的关键节点进行重点说明。在lk正常启动时会进入boot_linux_from_mmc。

2. boot_linux_from_mmc

boot_linux_from_mmc主要完成了bootimg读取到缓存,解压kernel,重定位kernel, ramdisk, dtb,并最终启动kernel,启动的同时会向kernel传递dtb地址,dtb的chosen保存了cmdline, 同时保存了ramdisk的起止地址。

  1. check_format_bit

  2. get_ffbm

  3. uhdr = (struct boot_img_hdr *)EMMC_BOOT_IMG_HEADER_ADDR;
    此处获取到了boot image头部的信息所在的内存地址,通过调用 memcmp(uhdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE),来判断0x8F6FF000 (EMMC_BOOT_IMG_HEADER_ADDR)是否和 boot.img 头的 MAGIC 值"ANDROID!" 相同。如果相同,则直接按照这个内存地址来启动系统,不再从emmc 中读取,直接启动Linux

  4. mmc_read(ptn + offset, (uint32_t *) buf, page_size):从boot/recovery分区读取boot_img_hdr到全局变量buf中

  5. 对读取的内容进行基本的合法判断:
    (1)header->MAGIC是否是ANDROID, 如果不是则异常退出
    (2)读取到的header->page_size是否与PAGE_SIZE相等,如果不相等,根据读取到的header->page_size对page_size全局变量重新赋值,重新赋值page_mask

  6. 对读取到的image header的kernel大小,ramdisk大小 向上取到PAGE_SIZE的整数倍
    kernel_actual = ROUND_TO_PAGE(hdr->kernel_size, page_mask);
    ramdisk_actual = ROUND_TO_PAGE(hdr->ramdisk_size, page_mask);
    second_actual = ROUND_TO_PAGE(hdr->second_size, page_mask);

  7. mage_addr = (unsigned char *)target_get_scratch_address()初始化image缓存地址,用于存放从boot分区读取到的image。image_addr这个值是 boot.img 在内存的缓存地址,缓存的地址由 SCRATCH_ADDR 宏指定,这个宏定义在target/msm8916/rules.mk 文件中,我们看到这个地址实际为0x91E00000

  8. memcpy(image_addr, (void *)buf, page_size); 前面说全局变量buf中存放boot_img_hdr,此处将其拷贝到image_addr中,大小为1个page

  9. imagesize_actual = (page_size + kernel_actual + ramdisk_actual + second_actual + dt_actual);
    初始化image大小,page_size为image header大小,kernel, ramdisk, dt大小均从boot image header中获取。dt大小在mkbootimg命令运行时写入,看mkbootimg中注释说是假设是一个page

  10. boot_verifier_init

  11. check_aboot_addr_range_overlap((uint32_t) image_addr, imagesize_actual)
    检查打算存放boot image的内存地址与当前运行的aboot是否有地址空间重叠部分

  12. mmc_read(ptn + offset, (void *)(image_addr + offset), imagesize_actual - page_size)
    从boot/recovery分区读取镜像到image_addr地址(不读取image_header和signature)

  13. mmc_read(ptn + offset, (void *)(image_addr + offset), page_size)
    对于需要验证签名的bootimage,还需要将签名读取到起始地址为image_addr,此处offset为imagesize_actual的位置,也就是将签名读取到image_addr+imagesize_actual的位置

  14. verify_signed_bootimg((uint32_t)image_addr, imagesize_actual);
    对上述签名进行验证

  15. is_gzip_package((unsigned char *)(image_addr + page_size), hdr->kernel_size)
    判断内核是否是压缩的,其中page_size为boot image头部大小
    decompress((unsigned char *)(image_addr + page_size),
    hdr->kernel_size, out_addr, out_avai_len,
    &dtb_offset, &out_len)
    如果内核是压缩的,则需要对内核进行解压缩,对kernel进行解压缩操作,解压缩后的内核存放到kernel_start_addr=image_addr +imagesize_actual+4k=0x91E00000+imagesize_actual+4k, kernel的大小为hdr->kernel_size
    注:imageaddr这个值是boot image 在内存的缓存地址,缓存的地址由 SCRATCH_ADDR 宏指定,这个宏定义在target/msm8916/rules.mk 文件中

到目前为止已经将boot.img读取到缓存地址0x91E00000处,并且将压缩的kernel解压到kernel_start_addr处

  1. update_ker_tags_rdisk_addr(hdr, IS_ARM64(kptr));
    一般情况下kernel地址,dtb地址,ramdisk地址由mkbootimage工具来指定为默认值,如果没有指定默认值则采用platform/msm8996/include/platform/iomap.h中定义的kernel地址,dtb地址,ramdisk地址来更新hdr(boot image header),此处使用了iomap.h中定义的如下:

其中DDR_STRT的地址为0x80000000

  1. hdr->kernel_addr = VA((addr_t)(hdr->kernel_addr));
    hdr->ramdisk_addr = VA((addr_t)(hdr->ramdisk_addr));
    hdr->tags_addr = VA((addr_t)(hdr->tags_addr));
    获取虚拟地址,此处由于没有开MMU,虚拟地址与物理地址相同

  2. check_aboot_addr_range_overlap
    检查要拷贝的目标kernel地址和ramdisk地址是否会与aboot重合

  3. 将解压后的kernel镜像和ramdisk镜像拷贝到image header所指定的位置,也就是重定位
    memmove((void*) hdr->kernel_addr, kernel_start_addr, kernel_size);
    memmove((void*) hdr->ramdisk_addr, (char *)(image_addr + page_size + kernel_actual), hdr->ramdisk_size);

  4. dev_tree_validate(table, hdr->page_size, &dt_hdr_size) 验证DTS

  5. dev_tree_get_entry_info(table, &dt_entry)

  6. decompress 如果dtb压缩了,则需要解压缩

  7. check_aboot_addr_range_overlap(hdr->tags_addr, dtb_size) 验证hdr->tags_addr是否会越界到aboot

  8. memmove((void *)hdr->tags_addr, (char *)best_match_dt_addr, dtb_size) 将解压后的dts拷贝到hdr->tags_addr

到目前为止,我们可以看到形成如下的内存布局,当前kernel,dtb,ramdisk分别位于如下红色框内.

至此我们所关心的几个问题:
Q: 磁盘中boot分区的boot.img被读取到内存的哪个位置?
A: image_addr这个值是boot.img 在内存的缓存地址,缓存的地址由 SCRATCH_ADDR宏指定,这个宏定义在target/msm8916/rules.mk 文件中,我们看到这个地址实际为0x91E00000

Q:bootimg中kernel, ramdisk,dtb的size如何确定?
A:主要是在编译时由mkbootimg工具通过读取输出目录中kernel和ramdisk文件的大小来获取,dtb大小默认为4k,一个page

Q: kernel解压后的应该放到内存的哪个位置? A: 通过boot.img存放地址为起始地址, 在偏移kernel_size +
ramdisk_size + dtb_size大小的位置 其中dtb默认为4K

Q:kernel解压后被重定位到哪个位置?
A:由LK的platform/msm8996/include/platform/iomap.h指定了kernel, ramdisk,
dtb的重定位的位置

注:看4.9的内核对于ARM64,编译的时候会将vmlinux拷贝成Image,然后将Image进行gzip压缩为Image.gz。之后通过cat将Image.gz和DTB写入Image.gz-dtb,Image.gz-dtb与ramdisk一起组成了boot.img,此处dtb应该是被与kernel打包l组合在一起

25.boot_linux((void *)hdr->kernel_addr, (void *)hdr->tags_addr, (const char *)hdr->cmdline, board_machtype(), (void *)hdr->ramdisk_addr, hdr->ramdisk_size)
从kernel_addr启动kernel,同时传递了如下几个参数:
kernel地址,dtb地址,cmdline地址,板子类型,ramdisk地址,ramdisk大小
注:此处的tags_addr就是dts地址

3.boot_linux

1.upadate_cmdline
解析当初通过mkbootimg命令传入的—cmdline参数,更新到final_cmdline中。命令mkbootimg通过—cmdline参数传递的值会被保存到boot.img的头部,此处就是通过解析boot.img的头部来更新final_cmdline

2.update_device_tree
主要更新了如下内容:
(1)更新了memory的起始地址和大小到DTS;
(2)将ramdisk起止地址更新到chosen

3.scm_elexec_call
调用执行Linux内核,此处可以看到,通过scm_arg传递了如下的信息:
(1)kernel地址
(2)dts地址, dtb中的chosen中保存了cmdline以及ramdisk的起止地址

4.通过SMC调用会进入到安全世界QSEE,并由安全世界引导kernel执行
注:trustzone可以理解为安全世界,它跑的代码为QSEE对标ARM的arm trust firmware

4.总结

  1. 从boot/recovery分区读取boot.img镜像到image_addr地址
  2. 解压kernel到kernel_start_addr地址处
  3. 重定位kernel ramdisk, dtb
  4. 解析并保存cmd_line
  5. 更新DTS,包括增加memory的起始地址和大小到DTS;将ramdisk起止地址更新到chosen
  6. 启动kernel,启动的同时会向kernel传递dtb地址,dtb的chosen保存了cmdline, 同时保存了ramdisk的起止地址,这样内核起来后会找到cmdline和ramdisk解析和挂载

高通8996启动流程-4. lk启动之boot_linux_from_mmc相关推荐

  1. 高通8996启动流程-4. lk启动之mkbootimg工具

    1.前言 Android在编译过程中会调用mkbootimg命令,mkbootimg是生成boo.img的命令,boot.img中包含了kernel, dtb, ramdisk等. 2. mkboot ...

  2. 深入理解Activity启动流程(三)–Activity启动的详细流程2

    本文原创作者:Cloud Chou. 欢迎转载,请注明出处和本文链接 本系列博客将详细阐述Activity的启动流程,这些博客基于Cm 10.1源码研究. 深入理解Activity启动流程(一)--A ...

  3. 深入理解Activity启动流程(二)–Activity启动相关类的类图

    本文原创作者:Cloud Chou. 欢迎转载,请注明出处和本文链接 本系列博客将详细阐述Activity的启动流程,这些博客基于Cm 10.1源码研究. 在介绍Activity的详细启动流程之前,先 ...

  4. App 启动流程与 Activity 启动流程梳理

    目录 前言 流程图 启动流程 第一阶段(Launcher 向 AMS 发送启动请求) 第二阶段(AMS 启动 Activity, 并告知 Launcher pasue) 第三阶段 (App 进程的 A ...

  5. 高通CamX-CHI关键流程

    深入理解高通 Camx CHI 架构 - SegmentFault 思否 高通CamX关键流程 - 云+社区 - 腾讯云 CamX关键流程 - 简书

  6. 高通8996启动流程-3. sbl1启动流程

    目录 1. 前言 2. sbl1总体流程 3.sbl1_main_ctl(pbl_shared)流程 4. boot_config_process_bl 4.1 Execute pre_procs 4 ...

  7. 高通8996启动流程-1.概述

    1.前言 本文档主要对MSM8996的启动流程进行一个简要的分析,目的在于展现启动流程的概貌,不会对每个细节做很详细的表述,但会对启动流程的关键节点进行重点说明. 2.关键术语 Hexagon Dig ...

  8. 高通8996启动流程-2.总体启动流程

    1.前言 本文档主要对MSM8996的启动流程进行一个简要的分析,目的在于展现启动流程的概貌,不会对每个细节做很详细的表述,但会对启动流程的关键节点进行重点说明.本文主要通过框图的方式展现bootlo ...

  9. linux uboot启动流程分析,uboot启动流程分析

    uboot版本为NXP维护的2016.03版本 下载地址为http://git.freescale.com/git/... 分析uboot的启动流程,需要编译一下uboot,然后打开链接脚本 u-bo ...

最新文章

  1. 如何才能识别市场趋势?[转]
  2. ArrayList遍历
  3. iOS中AVFoundation的简单使用—音乐的播放
  4. SAP CRM和C4C的产品主数据price维护
  5. Python并发编程:多进程-守护进程
  6. 【Python自动化运维之路Day6】
  7. Newtonsoft.Json 方法使用()
  8. java代码读取dbsequence的值_JDBC读取新插入Oracle数据库Sequence值的5种方法
  9. java通信方式_java 认知底层的五种通信方式
  10. python简单代码-用Python代码实现5种最好的、简单的数据可视化!
  11. C语言中将数字形式的字符串转换为数字的方法
  12. 企业微信--扫一扫功能(隐形坑)
  13. viterbi 中文分词-超简单版
  14. 三十六计之借刀杀人(第三计)
  15. [软件人生]抢钱的电影与现在的软件开发
  16. PNI12927磁场强度传感器--金属检测实现方案
  17. 电脑重启bootmgr_Windows系统启动不了如何修复?Bootmgr/NTLDR is missing解决方法
  18. 破解XCode 3.2.5 免证书运行程序到 真机ipod(已破解)
  19. 自学python能干些什么副业-工作多年,总结出几个比较野的副业路子!
  20. 股票日内交易策(源码)

热门文章

  1. uniapp小程序实现弹幕功能
  2. PPIO PRoute —— 为当下全球互联网量身定做的智能路由
  3. 个人感悟—来自Google的TCP BBR拥塞控制算法解析
  4. Xcode 8新功能介绍
  5. mysql btree索引原理_Postgres BTREE索引原理简单介绍
  6. Mac Maya2017 ViewCube(视图盒子)不显示问题解决
  7. linux运维可视化工具,试用Grafana:一个自动化运维常用的可视化开源工具
  8. Windows10,夜间模式失效?
  9. winserver搭建smtp_如何在服务器搭建本地smtp邮件服务
  10. PPT母版怎么应用到每张幻灯片?