关于imx8mq的bootloader系列,可以参考前面的三篇文章
imx8mq - u-boot-spl启动分析
imx8mq - bootloader编译过程
imx8mq - u-boot启动分析
nxp提供的imx8mq启动方式Bootloader方式如上图所示。
其中:
u-boot作为一款通用的Bootloader,其支持多种架构系列,支持多种嵌入式系统内核的引导,还具有丰富的功能配置,调试组件等等。所以,在系统硬件调试阶段,往往可以发挥强大的作用。
但是,对于有快启要求下,就需要对u-boot进行优化与裁剪了。甚至,对于一些简单的SOC架构,如S3C2440,完全可以自己写出一个最小的Bootloader,具备以下功能。可以参考韦老师的最小Bootloader
但是对于imx8系列的处理器,要从0编写一个Bootloader难度较大。还是选择在原有u-boot架构上根据以上思路进行裁剪优化就行。
根据图一所示的启动流程,能够动手优化的就u-boot的spl阶段和第二阶段。中间还有一段atf部分代码。本次优化的思路是,直接使用dd命令把内核和设备树镜像文件烧写到eMMC的硬件分区User的固定偏移地址上。然后spl读取内核和设备树到内存,在跳转到atf部分代码。u-boot第二阶段直接使用一个简单的跳转指令代替。下面一一列出代码:
这里代码有点多,不是实际需要就不用看了,代码作用就是把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();
…
}
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