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
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);
几个在board_init_r会用到的重要结构体
struct spl_image_info {
const char *name; //名称
u8 os; //OS类型
ulong load_addr; //加载地址
ulong entry_point; //入口
u32 size; //image大小
u32 flags; //标志
};
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);
};
struct spl_boot_device {
uint boot_device; //类型为:BOOT_DEVICE_XXX
const char *boot_device_name; //不重要,可以为空
};
/*
* 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(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_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 |