imx8mq-evk快速启动-bootloader时间优化(eMMC启动)

一、imx8mq的bootloader分析

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

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

Bootloader的启动流程图

imx8mq-evk快速启动-bootloader时间优化(eMMC启动)_第1张图片

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 
#include 
#include 
#include 
/*------------------------------------------------------------------------------------------------*/
#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_MMC
	struct udevice *dev;
#endif
	int 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_SUPPORT
		printf("spl: could not initialize mmc. error: %d\n", err);
#endif
		return err;
	}

#ifdef CONFIG_DM_MMC
	err = 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;
#endif
	if (err) {
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
		printf("spl: could not find mmc device. error: %d\n", err);
#endif
		return 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);
	else
		printf("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){ // > 30MB
		printf("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");
#else
    count = 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);
#endif
	op->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_INIT
	spl_board_init();
#endif
    /* 准备kernel启动环境 */
boot_linux_form_spl();
…
}

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

1、跳转指令

.text
.global _start
_start:
    mov	x0, #0x43000000
	mov	x1, #0
	mov	x2, #0
	mov	x3, #0
	mov	x4, #0x40480000
	mov	x5, #0x00000001
	br 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.o

u-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

你可能感兴趣的:(i.MX8mq)