imx8mq - u-boot-spl启动分析

Step 1: _start:

  • 保存一下32个64位寄存器到rom_pointer上
  • 设置小端字节序,关闭MMU,I/DCACHE,设置缓存一致性
  • 修复内核勘误bug
  • 使无效Cache/BPB/TLB
  • 低级初始化,spl不作处理
  • 转_main
Code in arch\arm\cpu\armv8\start.S
_start:
    /* Allow the board to save important registers */
	b	save_boot_params
	
	/*
	 * Could be EL3/EL2/EL1, Initial State:
	 * Little Endian, MMU Disabled, i/dCache Disabled
	 */
	...
	
	/* Apply ARM core specific erratas */
	bl	apply_core_errata
	
	/*
	 * Cache/BPB/TLB Invalidate
	 * i-cache is invalidated before enabled in icache_enable()
	 * tlb is invalidated before mmu is enabled in dcache_enable()
	 * d-cache is invalidated before enabled in dcache_enable()
	 */

	/* Processor specific initialization */
	bl	lowlevel_init
	    /* 在spl阶段不作处理 */
	bl _main

Step 2: _main:

ENTRY(_main)
/*
 * Set up initial C runtime environment and call board_init_f(0).
 * spl使用的堆栈区位于map:[0018_0000,0018_7FFF] size:32KB name:OCRAM_S
 */
    ldr	x0, =(CONFIG_SPL_STACK)
    mov	x0, sp
    
    /* 预留一个位置给gd结构体 */
    bl	board_init_f_alloc_reserve
    
    /* 预留 0x2000空间用于early malloc*/
    bl	board_init_f_init_reserve
    
    /*  */
    bl	board_init_f
        /* Clear global data */
	    memset((void *)gd, 0, sizeof(gd_t));
	    
	    /* CPU初始化:系统定时器,内核时钟频率,关闭看门狗 */
	    arch_cpu_init();
	    board_early_init_f();
	    timer_init();
	    
	    /* 串口初始化,输出:U-Boot SPL 2017.03-g2537522 (May 16 2019 - 14:50:40) */
	    preloader_console_init();
	    
	    /* Clear the BSS */
	    memset(__bss_start, 0, __bss_end - __bss_start);
	    
	    /* 跳转第二阶段 */
	    board_init_r(NULL, 0);

Step 3: board_init_r

几个在board_init_r会用到的重要结构体

  1. spl引导的第二阶段代码的image信息
struct spl_image_info {
	const char *name;       //名称
	u8 os;                  //OS类型
	ulong load_addr;        //加载地址
	ulong entry_point;      //入口
	u32 size;               //image大小
	u32 flags;              //标志
};
  1. 保存有关加载SPL映像的方法的信息
struct spl_image_loader {
	const char *name;
/* 可选的启动设备
enum {
	BOOT_DEVICE_RAM,
	BOOT_DEVICE_MMC1,
	BOOT_DEVICE_MMC2,
	BOOT_DEVICE_MMC2_2,
	BOOT_DEVICE_NAND,
	BOOT_DEVICE_ONENAND,
	BOOT_DEVICE_NOR,
	BOOT_DEVICE_UART,
	BOOT_DEVICE_SPI,
	BOOT_DEVICE_USB,
	BOOT_DEVICE_SATA,
	BOOT_DEVICE_I2C,
	BOOT_DEVICE_BOARD,
	BOOT_DEVICE_DFU,
	BOOT_DEVICE_NONE
};
*/
	uint boot_device;
	/**
	 * load_image() - Load an SPL image
	 *
	 * @spl_image: place to put image information
	 * @bootdev: describes the boot device to load from
	 */
	int (*load_image)(struct spl_image_info *spl_image,
			  struct spl_boot_device *bootdev);
};
  1. 描述SPL使用的引导设备
struct spl_boot_device {
	uint boot_device;                //类型为:BOOT_DEVICE_XXX
	const char *boot_device_name;    //不重要,可以为空
};
  1. 从设备加载数据所需的信息
/*
 * Information required to load data from a device
 *
 * @dev: Pointer to the device, e.g. struct mmc *
 * @priv: Private data for the device
 * @bl_len: Block length for reading in bytes
 * @filename: Name of the fit image file.
 * @read: Function to call to read from the device
 */
struct spl_load_info {
	void *dev;
	void *priv;
	int bl_len;
	const char *filename;
	ulong (*read)(struct spl_load_info *load, ulong sector, ulong count,
		      void *buf);
};

board_init_r代码分析

board_init_r(NULL, 0)
    /* 启动清单,设定可以从哪里启动。本次只是设置第一个, */
    u32 spl_boot_list[] = {BOOT_DEVICE_NONE,BOOT_DEVICE_NONE,BOOT_DEVICE_NONE,BOOT_DEVICE_NONE};
    
    /* spl引导的第二阶段代码的image信息 */
	struct spl_image_info spl_image;
	
    /*分配内存用于malloc,  map:[0x00182000,0x00184000] size:8KB */
	mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,
			CONFIG_SYS_SPL_MALLOC_SIZE);
	gd->flags |= GD_FLG_FULL_MALLOC_INIT;
	
	/* 设置gd结构体 malloc信息,标志spl_init已经被调用 */
	spl_init()
	
	/*  */
	spl_board_init();
	    /* 使能TZASC */
	    enable_tzc380();
	    
	    /* 配置电源管理I2C,初始化电源管理。串口输出PMIC:  PFUZE100 ID=0x10 */
	        /* Adjust pmic voltage to 1.0V for 800M */
	    setup_i2c(0, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info1);
	    power_init_board();
	    
	    /* DDR initialization */
	    spl_dram_init();
	    
	    /* 串口打印 */
	    puts("Normal Boot\n");
	    
/* 找到一个spl_image_info */
	
	/* 清零spl_image */
	memset(&spl_image, '\0', sizeof(spl_image));
	
	/* 设置启动引导设备 */
	board_boot_order(spl_boot_list);
	
	/* 从spl_boot_list清单加载一个spl_image */
	boot_from_devices(&spl_image, spl_boot_list,ARRAY_SIZE(spl_boot_list));
	    /* 当从eMMC启动时,loader参考SPL_LOAD_IMAGE_METHOD("MMC1", 0, BOOT_DEVICE_MMC1, spl_mmc_load_image);
	     * 此时,loader->load_image = spl_mmc_load_image
	     */
	    loader = spl_ll_find_loader(spl_boot_list[i]);
	    printf("Trying to boot from %s\n", loader->name);//"MMC1"
	    
	    /* 通过loader->load_image设置spl_image,此时loader->load_image指向spl_mmc_load_image */
	    spl_load_image(spl_image, loader)
	        struct spl_boot_device bootdev;
	       	bootdev.boot_device = loader->boot_device;
	        bootdev.boot_device_name = NULL;
	        loader->load_image(spl_image, &bootdev);//参考下小节
    spl_board_prepare_for_boot();/* 啥也没做 */
    jump_to_image_no_args(&spl_image);
    	typedef void __noreturn (*image_entry_noargs_t)(void);

	    image_entry_noargs_t image_entry = 	(image_entry_noargs_t)spl_image->entry_point;

	    debug("image entry point: 0x%lX\n", spl_image->entry_point);
	    image_entry();
	        
/* 至此,SPL的使命结束了,跳转的地址为bl31.bin的入口地址:0x00910000 接下来到bl31.bin发光发热了。 
*/
	

分析spl_mmc_load_image(spl_image, &bootdev);

/* spl_image:返回的spl_image指针
 * bootdev:其boot_device = BOOT_DEVICE_MMC1;
 */
spl_mmc_load_image(spl_image, &bootdev)
    struct mmc *mmc = NULL;
    u32 boot_mode;
    
    /* mmc初始化 */
    spl_mmc_find_device(&mmc, bootdev->boot_device);
    mmc_init(mmc);
    boot_mode = MMCSD_MODE_EMMCBOOT = spl_boot_mode(bootdev->boot_device);
    
    /* 切换硬件分区为第一个boot分区,里面烧写了完整的uboot等第二阶段引导程序 
     * The MMC standard provides for two boot partitions (numbered 1 and 2),
	 * rpmb (3), and up to 4 addition general-purpose partitions (4-7).
	 */
    blk_dselect_hwpart(mmc_get_blk_desc(mmc), part = 0);
    //0x300* 512 = 0x60000 = 384KB,该地址由imx-mkimage中的soc.mak确定
    mmc_load_image_raw_sector(spl_image, mmc, CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR = 0x300);
        struct image_header *header;
        /* 读取uboot.itb的头部信息 */
        blk_dread(mmc_get_blk_desc(mmc), sector, 1, header);
        
        printf("Found FIT\n");
        load.dev = mmc;
		load.priv = NULL;
		load.filename = NULL;
		load.bl_len = mmc->read_bl_len;
		load.read = h_spl_load_read;
		ret = spl_load_simple_fit(spl_image, &load, sector, header);    
		    /* 读取完整的fit信息
		     * debug("fit read sector %lx, sectors=%d, dst=%p, count=%lu, size=0x%lx\n", sector, sectors, fit, count, size);
		     * fit read sector 300, sectors=2, dst=00000000401ffa40, count=2, size=0x370
		     */
		    
		    count = info->read(info, sector, sectors, fit);
		    
		    /* 获取fit中的image段 */
		    images = fdt_path_offset(fit, FIT_IMAGES_PATH);
		     
		    /* spl_fit_get_image_node(const void *fit, int images, const char *type, int index)
		     * fit:    设备树首地址。
		     * image:  /image子节点的index
		     * type:   configurations子节点中的某一个属性名字
		     * index:  property的第几个value
		     */
		     /* firmware = "uboot@1"; 获取uboot@1的index */
		    node = spl_fit_get_image_node(fit, images, "firmware", 0);
		    
		    /* 从node节点描述中加载image:也就是把u-boot-nodtb.bin加载到0x40200000上
		     * 然后设置好传入的spl_image_info结构体
		     */
		    spl_load_fit_image(info, sector, fit, base_offset, node, spl_image);
		    
		    spl_image->os = IH_OS_U_BOOT;
		    
		    
		    /* fdt = "fdt@1"; 获取fdt@1的index */
		    node = spl_fit_get_image_node(fit, images, FIT_FDT_PROP, 0);
		    
		    /* 以64字节(cache_line)对齐加载fit放置在 */
		    image_info.load_addr = spl_image->load_addr + spl_image->size;
		    ret = spl_load_fit_image(info, sector, fit, base_offset, node,
		    
		    /* loadables = "atf@1"; 获取atf@1的index ,在这里,如果loadables不止一个,则继续加载下一个 */
		    node = spl_fit_get_image_node(fit, images, "loadables", index);
		    
		    /* aft位于map[0090_0000,0091_FFFF] size:128KB name:OCRAM 的0x00910000 */
		    ret = spl_load_fit_image(info, sector, fit, base_offset, node,&image_info);
		    
		    /* 设置spl_image信息 */
		    spl_image->entry_point = spl_image->load_addr;
		    spl_image->flags |= SPL_FIT_FOUND;
    return 0;
/* 至此, spl_mmc_load_image结束,所有FIT描述的段各归各位,准备接下来的跳转
 */

u-boot.itb的内存映象为:

map size content
0000_0000 0000_0370 0x370 FIT
0000_0370 0000_3000 0x2C90 0
0000_3000 0009_3140 0x90140 u-boot-nodtb.bin
0009_3140 0009_F7D8 0xC698 bl31.bin
0009_F7D8 000A_691D 0x7145 fsl-imx8mq-evk.dtb

spl_mmc_load_image的功能就是把u-boot-nodtb.bin,bl31.bin,fsl-imx8mq-evk.dtb加载到内存中。
加载内存映象为:

content destination size
bl31.bin 0x910000 0xc698
u-boot-nodtb.bin 0x40200000 0x90140
fsl-imx8mq-evk.dtb 40290140 0x7145

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