一、imx8mq的bootloader分析

关于imx8mq的bootloader系列,可以参考前面的三篇文章

imx8mq - u-boot-spl启动分析
imx8mq - bootloader编译过程
imx8mq - u-boot启动分析

Bootloader的启动流程图

nxp提供的imx8mq启动方式Bootloader方式如上图所示。
其中:

  • Power on sequence为芯片上电的上电过程,此部分时间基本上为一个定值,也没有什么时间上的优化空间
  • BootROM:固化在SOC上的一段启动引导代码,用于引导系统根据BOOTMODE和BOOTCFG配置引导系统以不同的方式从不同的启动设备启动。也没有什么优化空间。毕竟代码已经固化死了。
  • Signal hdmi:一段初始化HDMI设备的固化代码,对HDMI部分不熟悉,而且,NXP上提供的也是bin文件。所以,基本也没什么优化空间。
  • spl-boot:用于引导uboot初始化SOC必须的部分,比如DDR,然后复制bl31,uboot,到系统内存,引导下一步启动bl31。这部分可以进行一些硬件IO时间的优化,比如读取EMMC优化,串口打印优化。
  • bl31:ATF部分代码,用于引导RAM-v8启动kernel。这一段没什么研究。虽然提供源码,但是本次优化不对这部分进行优化。
  • uboot:这里为Bootloader阶段优化的主要优化点。可以优化的地方有很多。Uboot的主要作用是引导kernel启动,其他部分都可以进行裁剪,比如串口打印,USB相关初始化,网络初始化,QSPI部分初始化,一些cmd裁剪。镜像文件拷贝的IO优化等等

二、uboot快速启动方案:

1.方案讨论

u-boot作为一款通用的Bootloader,其支持多种架构系列,支持多种嵌入式系统内核的引导,还具有丰富的功能配置,调试组件等等。所以,在系统硬件调试阶段,往往可以发挥强大的作用。

但是,对于有快启要求下,就需要对u-boot进行优化与裁剪了。甚至,对于一些简单的SOC架构,如S3C2440,完全可以自己写出一个最小的Bootloader,具备以下功能。可以参考韦老师的最小Bootloader

  • SOC级硬件初始化
  • 初始化DDR
  • 初始化启动设备接口以及启动设备初始化
  • 从启动设备读取内核和设备树到内存
  • 跳转启动内核

但是对于imx8系列的处理器,要从0编写一个Bootloader难度较大。还是选择在原有u-boot架构上根据以上思路进行裁剪优化就行。

2. imx8系列的u-boot启动内核

根据图一所示的启动流程,能够动手优化的就u-boot的spl阶段和第二阶段。中间还有一段atf部分代码。本次优化的思路是,直接使用dd命令把内核和设备树镜像文件烧写到eMMC的硬件分区User的固定偏移地址上。然后spl读取内核和设备树到内存,在跳转到atf部分代码。u-boot第二阶段直接使用一个简单的跳转指令代替。下面一一列出代码:

第一部分:SPL阶段把所需镜像文件拷贝到内存

这里代码有点多,不是实际需要就不用看了,代码作用就是把spl后面的bl31,用来跳转kernel的miniboot,kernel,dtb四个镜像文件到内存指定位置上。

#include <mmc.h>
#include <mapmem.h>
#include <div64.h>
#include <linux/math64.h>
/*------------------------------------------------------------------------------------------------*/
#define MINIBOOT_LOAD_ADDR      ((void*)0x40200000)
#define ATF_LOAD_ADDR               ((void*)0x00910000)
#define DTB_LOAD_ADDR           ((void*)0x43000000)
#define IMAGE_LOAD_ADDR         ((void*)0x40480000)#define ATF_MMC_SECTOR_OFFSET        0x00000400      //1024
#define MINIBOOT_MMC_SECTOR_OFFSET  0x00000800  //2048
#define DTB_MMC_SECTOR_OFFSET       0x00001000      //4096
#define IMAGE_MMC_SECTOR_OFFSET     0x00005000      //20480#define ATF_SECTOR_COUNT         0x00000064  //51.2KB
#define MINIBOOT_SECTOR_COUNT   0x00000001  //512B
#define DTB_SECTOR_COUNT            0x00000064  //51.2KB
#define IMAGE_SECTOR_COUNT      0x0000C800  //25MB (no use)#define EMMC_PART               0#define LINUX_ARM64_IMAGE_MAGIC 0x644d5241
#define FDT_MAGIC   0xd00dfeed  /* 4: version, 4: total size */struct spl_boot_opr{ulong ep;    /* entry point if OS */ulong dp;    /* dtb point in ram  */
};
/* See Documentation/arm64/booting.txt in the Linux kernel */
struct Image_header {uint32_t   code0;      /* Executable code */uint32_t   code1;      /* Executable code */uint64_t   text_offset;/* Image load offset, LE */uint64_t image_size; /* Effective Image size, LE */uint64_t  res1;       /* reserved */uint64_t  res2;       /* reserved */uint64_t  res3;       /* reserved */uint64_t  res4;       /* reserved */uint32_t  magic;      /* Magic number */uint32_t  res5;
};static void dump_image_head(struct Image_header *ih)
{printf("Image_header->code0\t:0x%x\n",ih->code0);printf("Image_header->code1\t:0x%x\n",ih->code1);printf("Image_header->text_offset\t:0x%llx\n",ih->text_offset);printf("Image_header->image_size\t:0x%llx\n",ih->image_size);printf("Image_header->magic\t:0x%x\n",ih->magic);
}
static void dump_dtb_head(struct fdt_header *fdt)
{printf("fdt_header->magic\t:0x%x\n",fdt_magic(fdt));printf("fdt_header->totalsize\t:0x%x\n",fdt_totalsize(fdt));printf("fdt_header->version\t:0x%x\n",fdt_version(fdt));
}
static int booti_verify(struct spl_boot_opr *op)
{struct Image_header *ih;struct fdt_header *dh;ih = (struct Image_header *)map_sysmem(op->ep, 0);dh = (struct fdt_header *)map_sysmem(op->dp, 0);if (ih->magic != le32_to_cpu(LINUX_ARM64_IMAGE_MAGIC)) {printf("Bad Linux ARM64 Image magic!\n");dump_image_head(ih);return -1;}if (fdt_magic(dh) != FDT_MAGIC){printf("Bad DTB magic!\n\tfdt_magic(fdt) = 0x%x;\tFDT_MAGIC = 0x%x\n",fdt_magic(dh) ,FDT_MAGIC);return -1;}return 0;
}
static int spl_mmc_find_device(struct mmc **mmcp, u32 boot_device)
{
#ifdef CONFIG_DM_MMCstruct udevice *dev;
#endifint err, mmc_dev;mmc_dev = 0;//spl_mmc_get_device_index(boot_device);if (mmc_dev < 0)return mmc_dev;err = mmc_initialize(NULL);if (err) {
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORTprintf("spl: could not initialize mmc. error: %d\n", err);
#endifreturn err;}#ifdef CONFIG_DM_MMCerr = uclass_get_device(UCLASS_MMC, mmc_dev, &dev);if (!err)*mmcp = mmc_get_mmc_dev(dev);
#else*mmcp = find_mmc_device(mmc_dev);err = *mmcp ? 0 : -ENODEV;
#endifif (err) {
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORTprintf("spl: could not find mmc device. error: %d\n", err);
#endifreturn err;}return 0;
}
//#define TIME_DEBUG
static int read_image(struct spl_boot_opr *op)
{struct mmc *mmc = NULL;int dev= 0 , part = 0;unsigned long count, times;int err = 0;struct Image_header *ih;struct fdt_header *dh;void *dtb_desc, *image_desc;dtb_desc = DTB_LOAD_ADDR;image_desc = IMAGE_LOAD_ADDR;ih = IMAGE_LOAD_ADDR;dh = DTB_LOAD_ADDR;//寻找mmc控制器结构体printf("spl: Start read_image ,\n find mmc from BOOT_DEVICE_MMC1\n");err = spl_mmc_find_device(&mmc, BOOT_DEVICE_MMC1);if (err){printf("spl: can't find any mmc devide from : %d\n\terr:%d\n", BOOT_DEVICE_MMC1,err);return -1;}//mmc接口初始化printf("spl: Get mmc:%s and then init mmc\n",mmc->cfg->name);mmc->has_init = 0;err = mmc_init(mmc);if (err) {printf("spl: mmc init failed with error: %d\n", err);return -1;}//选择用户分区part = EMMC_PART;err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part);printf("switch to partitions #%d, %s\n", part, (!err) ? "OK" : "ERROR");if (err < 0){printf("spl: Switch mmc part failed with error: %d\n", err);return -1;}if (mmc->part_config == MMCPART_NOAVAILABLE)printf("mmc%d is current device\n", dev);elseprintf("mmc%d(part %d) is current device\n", dev, mmc_get_blk_desc(mmc)->hwpart);
#ifdef TIME_DEBUG              //读取Image的头部信息,判断Image大小times = get_timer(0);count = blk_dread(mmc_get_blk_desc(mmc), IMAGE_MMC_SECTOR_OFFSET, 1, ih);times = get_timer(times);if (count != 1){printf("spl: Read Image head error: %ld\n", count);return -1;}printf("%lu bytes read in %lu ms", count * 512, times);if (times > 0) {puts(" (");print_size(div_u64(count * 512, times) * 1000, "/s");puts(")");}puts("\n");//打印头部信息dump_image_head(ih);if (ih->image_size > 0x1E00000){ // > 30MBprintf("Image size > 0x1E00000 \n");return -1;}//读取Image到内存times = get_timer(0);count = blk_dread(mmc_get_blk_desc(mmc), IMAGE_MMC_SECTOR_OFFSET,
ih->image_size / 512 + 1 , image_desc);times = get_timer(times);if (count != (ih->image_size / 512 + 1)) {printf("spl: can't read Image from mmc;count:%ld\n", count);return -1;}printf("%lu bytes read in %lu ms", count * 512, times);if (times > 0) {puts(" (");print_size(div_u64(count * 512, times) * 1000, "/s");puts(")");}puts("\n");//读取dtb到内存times = get_timer(0);count = blk_dread(mmc_get_blk_desc(mmc), DTB_MMC_SECTOR_OFFSET,
DTB_SECTOR_COUNT, DTB_LOAD_ADDR);times = get_timer(times);if (count != (fdt_totalsize(dh) / 512 + 1)){printf("spl: can't read dtb from mmc;count:%ld\n", count);return -1;}printf("%lu bytes read in %lu ms", count * 512, times);if (times > 0) {puts(" (");print_size(div_u64(count * 512, times) * 1000, "/s");puts(")");}puts("\n");//读取atf到内存times = get_timer(0);count = blk_dread(mmc_get_blk_desc(mmc), ATF_MMC_SECTOR_OFFSET,
ATF_SECTOR_COUNT, ATF_LOAD_ADDR);times = get_timer(times);if (count != ATF_SECTOR_COUNT){printf("spl: can't read bl31 from mmc;count:%ld\n", count);return -1;}printf("%lu bytes read in %lu ms", count * 512, times);if (times > 0) {puts(" (");print_size(div_u64(count * 512, times) * 1000, "/s");puts(")");}puts("\n");//读取miniboot到内存times = get_timer(0);count = blk_dread(mmc_get_blk_desc(mmc), MINIBOOT_MMC_SECTOR_OFFSET,
MINIBOOT_SECTOR_COUNT, MINIBOOT_LOAD_ADDR);times = get_timer(times);if (count != MINIBOOT_SECTOR_COUNT){printf("spl: can't read miniboot from mmc;count:%ld\n", count);return -1;}printf("%lu bytes read in %lu ms", count * 512, times);if (times > 0) {puts(" (");print_size(div_u64(count * 512, times) * 1000, "/s");puts(")");}puts("\n");
#elsecount = blk_dread(mmc_get_blk_desc(mmc), IMAGE_MMC_SECTOR_OFFSET, 1, ih);count = blk_dread(mmc_get_blk_desc(mmc), IMAGE_MMC_SECTOR_OFFSET, ih->image_size / 512 + 1 , image_desc);count = blk_dread(mmc_get_blk_desc(mmc), DTB_MMC_SECTOR_OFFSET, DTB_SECTOR_COUNT, DTB_LOAD_ADDR);count = blk_dread(mmc_get_blk_desc(mmc), ATF_MMC_SECTOR_OFFSET, ATF_SECTOR_COUNT, ATF_LOAD_ADDR);count = blk_dread(mmc_get_blk_desc(mmc), MINIBOOT_MMC_SECTOR_OFFSET, MINIBOOT_SECTOR_COUNT, MINIBOOT_LOAD_ADDR);
#endifop->ep = (ulong)image_desc;op->dp = (ulong)dtb_desc;return booti_verify(op);
}
void boot_linux_form_spl(void)
{int err;struct spl_boot_opr opr;typedef void __noreturn (*image_entry_noargs_t)(void);image_entry_noargs_t image_entry = (image_entry_noargs_t)ATF_LOAD_ADDR;//读取,验证err = read_image(&opr);if (err < 0){printf("read_image error:%d\ncomtinue to run spl\n",err);}else{printf("prepare to jump linux: 0x%p\n", ATF_LOAD_ADDR);image_entry();}
}
/*------------------------------------------------------------------------------------------------*/
void board_init_r(gd_t *dummy1, ulong dummy2)
{…
#ifdef CONFIG_SPL_BOARD_INITspl_board_init();
#endif/* 准备kernel启动环境 */
boot_linux_form_spl();
…
}

第二部分 用来代替u-boot的miniboot

1、跳转指令

.text
.global _start
_start:mov  x0, #0x43000000mov  x1, #0mov   x2, #0mov   x3, #0mov   x4, #0x40480000mov  x5, #0x00000001br x4

2、连接脚本

OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(_start)
SECTIONS {. = 0x40200000;.text : { *(.text) }. = ALIGN(4);.rodata : {*(.rodata*)} . = ALIGN(4);.data : { *(.data) }. = ALIGN(4);__bss_start = .;.bss : { *(.bss)  *(COMMON) }__bss_end = .;
}

3、Makefile

CFLAGS   := -Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding
CPPFLAGS := objs := start.ou-boot.bin: $(objs)${LD} -Tboot.lds -o boot.elf $^${OBJCOPY} -O binary -S boot.elf $@${OBJDUMP} -D -m arm boot.elf > boot.dis%.o:%.c${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<%.o:%.S${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<clean:rm -f *.o *.bin *.elf *.dis

4、编译过程

$ source /opt/fsl-imx-x11/4.9.51-mx8-beta/environment-setup-aarch64-poky-linux
$ make
aarch64-poky-linux-gcc  --sysroot=/opt/fsl-imx-x11/4.9.51-mx8-beta/sysroots/aarch64-poky-linux  -Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -c -o start.o start.S
aarch64-poky-linux-ld  --sysroot=/opt/fsl-imx-x11/4.9.51-mx8-beta/sysroots/aarch64-poky-linux -Tboot.lds -o boot.elf start.o
aarch64-poky-linux-objcopy -O binary -S boot.elf u-boot.bin
aarch64-poky-linux-objdump -D -m arm boot.elf > boot.dis

imx8mq-evk快速启动-bootloader时间优化(eMMC启动)相关推荐

  1. TDA4 启动 修改为从emmc启动

    TDA4 7.03版本修改为从emmc 启动: 准备sd卡: 将一个空间大于8g的sd卡与pc连接, 先运行sudo fdisk -l来找出要格式化的 SD 卡, 然后执行 执行sudo /psdk_ ...

  2. 优化WinXP启动 加速开机时间(转)

    如开机时太多程序要运行时会影响开机的速度,先检查那些程序会在开机时运行 在[开始]→[运行]→键入[msconfig]→选[启动] 在启动内的程序是代表开机时要运行的程序,如须暂时停止运行某些程序便取 ...

  3. Android 系统性能优化(58)---开机时间优化

    优化启动时间 启动时间是系统性能的重要组成部分,因为用户必须等待启动完成后才能使用设备.对于较常进行冷启动的汽车等设备而言,较短的启动时间至关重要(没有人喜欢在等待几十秒后才能输入导航目的地). An ...

  4. 启动白屏处理_App启动优化一顿操作猛如虎

    前言 本次主要内容包括: 针对App启动优化我们做了哪些工作? 1.App启动优化方向:视觉体验优化 2.App启动优化方向:代码逻辑优化 一.App启动优化方向:视觉体验优化 App启动时白屏问题 ...

  5. Android性能优化之启动加速

    Android性能优化之启动加速 http://blog.csdn.net/mybook1122/article/details/65029259 http://blog.csdn.net/myboo ...

  6. Android性能优化之启动加速35%

    一.前言 随着项目版本的迭代,App 的性能问题会逐渐暴露出来,而好的用户体验与性能表现紧密相关. 下面小编就从应用的启动优化开始,根据实际案例,打造闪电般的 App 启动速度. 二.初识启动加速 对 ...

  7. Android性能优化之App应用启动分析与优化

    前言: 昨晚新版本终于发布了,但是还是记得有测试反馈app启动好长时间也没进入app主页,所以今天准备加个班总结一下App启动那些事! app的启动方式: 1.)冷启动      当启动应用时,后台没 ...

  8. android动态设置冷启动图片拉伸变形,Android冷启动时间优化

    冷启动时间是指当用户点击你的app那一刻到系统调用Activity.onCreate()之间的时间段.在这个时间段内,WindowManager会先加载app主题样式中的windowBackgroun ...

  9. vue“路由懒加载” 技术,让网页快速加载 (优化篇)

    (含代码示例.截图演示)让中大型vue项目,按需加载文件,让网页快速渲染! 官方文档:路与懒加载 所谓的路由懒加载: 代码示例 · 对比: 1. 没有优化的代码(截图1 · 省略) import Vu ...

最新文章

  1. icinga安装介绍,监控软件
  2. R语言修改标题、坐标轴刻度、坐标轴名称的大小(cex.axis、cex.lab、cex.main函数)...
  3. 数仓dw怎么建_网易严选如何打造数仓规范和评价体系
  4. 关于不过洋节的通知_蟠桃宫小学关于平安夜、圣诞节安全教育告家长通知书
  5. 使用scapy回放wireshark抓到的包
  6. python知识点总结
  7. 社交媒体广告看不出来?Instagram加标签让你一目了然
  8. 天津市全国计算机等级报名时间,天津市2018年全国计算机等级考试报名时间及地点...
  9. 接口做的好怎么形容_大连SIEMENS西门子M174接口plc模块
  10. 赋能行业 共建生态 阿里巴巴首次亮相义乌标准展
  11. nas 和 远程文件夹同步_群晖NAS同步文件夹功能打开有什么需要注意的?
  12. 高速EDA设计课程报告(二)
  13. 小米手机Root的过程及解决Unable to get view server version from device问题
  14. 程序员的九阳神功,学会逆天改命!
  15. uibot和按键精灵区别_uibot和按键精灵有什么区别?
  16. 50G-PON,继10G PON之后的新一代PON技术
  17. python+selenium3登录126邮箱并发送邮件
  18. 延迟满足 —— 达到目标需要忍住重重诱惑
  19. c语言中怎么调用自己定义的函数,c语言中怎么调用自己定义的函数?
  20. 基于weka的数据挖掘开发技术分析研究

热门文章

  1. 美国登月技术退步了?50年前就能载人着陆,怎么现在只能带着史努比绕一圈...
  2. 记:combotree中getValue和getText问题
  3. oracle平均值语句,Oracle / PLSQL AVG函数
  4. c语言格式占位符可以不用吗,C语言占位符(待完善)
  5. 动态获取本地json文件,渲染为表格-前端html+css+javascript,nth-child选择器,实时浏览插件,vscode,ajax
  6. 普及一个数分的领域,可能对你有用
  7. XMind, OneNote, Effie 哪款更适合记者?
  8. The Road to learn React书籍学习笔记(第一章)
  9. 联想小新i1000拆机图解_弃独显配锐炬集显 联想小新I1000时尚本评测
  10. 王者服务器维护导致的挂机扣分吗,王者荣耀文字检测系统是什么有用吗?玩家骂人挂机被扣分...