NXP i.MX8M secure boot流程
Uboot链接脚本分析述
Uboot启动分析–start.S启动分析(1)
Uboot启动分析–start.S启动分析(2)
Uboot启动分析–start.S启动分析(3)
Uboot启动分析–__main分析(1)
Uboot启动分析–__main分析(2)
Uboot启动分析–启动kernel
Uboot分析–SPL跳转过程分析
Uboot中lpddr4的初始化(i.MX8M)
使用U_BOOT_CMD()自定义uboot命令
一切就绪后进入交互状态,cli_init->cli_loop循环读取cmdline中的启动参数,将其存入console_buffer字符数组。若autoboot_command启动内核,则不会执行到cli_loop,若按键,则进入cli_loop函数,循环等待执行命令。
void main_loop(void)
{const char *s;bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");if (IS_ENABLED(CONFIG_VERSION_VARIABLE))env_set("ver", version_string); /* set version variable *///hush shell初始化cli_init();/* 处理延时参数 */s = bootdelay_process();if (cli_process_fdt(&s))cli_secure_boot_cmd(s);/* 若启动延时结束前,用户输入任意按键打断启动过程,则返回 *//* 否则启动 */autoboot_command(s);/* cli_loop返回,说明用户一段时间都没有任何输入 *//* 因此打印提示信息,提示没有获得命令 *///循环读取控制台输入的字符cli_loop();panic("No CLI available");
}
const char *bootdelay_process(void)
{char *s;int bootdelay;bootcount_inc();//去gd结构体中搜寻参数s = env_get("bootdelay");bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;debug("### main_loop entered: bootdelay=%dnn", bootdelay);bootretry_init_cmd_timeout();//暂时中断启动//选中altbootcmd函数并返回if (bootcount_error())s = env_get("altbootcmd");else//返回bootcmd这个变量的地址s = env_get("bootcmd");if (IS_ENABLED(CONFIG_OF_CONTROL))process_fdt_options(gd->fdt_blob);stored_bootdelay = bootdelay;//返回sreturn s;
}
运行boot命令
void autoboot_command(const char *s)
{debug("### main_loop: bootcmd="%s"n", s ? s : "<UNDEFINED>");if (s && (stored_bootdelay == -2 ||(stored_bootdelay != -1 && !abortboot(stored_bootdelay)))) {bool lock;int prev;lock = autoboot_keyed() &&!IS_ENABLED(CONFIG_AUTOBOOT_KEYED_CTRLC);if (lock)prev = disable_ctrlc(1); /* disable Ctrl-C checking *///boot命令run_command_list(s, -1, 0);if (lock)disable_ctrlc(prev); /* restore Ctrl-C checking */
}
uboot中有一个小巧的命令解释器hush shell,run_command_list只是对 hush shell 中的函数 parse_string_outer 进行了一层封装。parse_string_outer函数调用了bush_shell的命令解释器parse_stream_outer函数来解释bootcmd的命令,而环境变量bootcmd的启动命令用来设置linux必要的启动环境,board_run_command执行命令。然后加载和启动内核。那么board_run_command如何执行命令呢?
board_run_command函数通过bootcmd参数中的bootm命令找到cmdbootm.c中的bootm_maybe_autostart函数中的
rcode = parse_string_outer(buff, flag);
rcode = board_run_command(buff);
mkimage在制作映象文件的时候,是在原来的可执行映象文件的前面加上一个0x40字节的头,记录参数所指定的信息,这样uboot才能识别这个映象是针对哪个CPU体系结构的,哪个OS的,哪种类型,加载内存中的哪个位置, 入口点在内存的那个位置以及映象名是什么。
parse_string_outer->run_list->cmd_process,parse_string_outer最终会调用到cmd_process处理命令。
U-Boot的每一个命令都是通过U_Boot_CMD宏定义的。这个宏在头文件中定义
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)
cmd_tbl_t _u_boot_cmd##name Struct_Section = {#name, maxargs, rep, cmd, usage}
其中:
· name:命令的名字,他不是一个字符串,不能用双引号括起来
· maxargs:最大的参数个数
· command:对应的函数指针
· usage:一个字符串,简短的使用说明
· help:一个字符串,比较详细的使用说明
以启动参数中的booti命令为例,调用find_cmd在命令表中找到指定的命令,
U_BOOT_CMD(booti, CONFIG_SYS_MAXARGS, 1, do_booti,"boot Linux kernel 'Image' format from memory", booti_help_text
);
bootm_headers结构体存储了os image信息、os的入口、ramdisk的起始地址、设备树地址和长度、cmdline的起始位置(传给内核)。
typedef struct bootm_headers {image_header_t *legacy_hdr_os; /* image header pointer */image_header_t legacy_hdr_os_copy; /* header copy */ulong legacy_hdr_valid;image_info_t os; /* os image info */ulong ep; /* os入口 */ulong rd_start, rd_end;/* ramdisk 开始和结束位置 */char *ft_addr; /* 设备树地址 */ulong ft_len; /* 设备树长度 */ulong initrd_start;/* initrd 开始位置 */ ulong initrd_end;/* initrd 结束位置 */ ulong cmdline_start;/* cmdline 开始位置 */ ulong cmdline_end;/* cmdline 结束位置 */struct bd_info *kbd;} bootm_headers_t;
image_info头信息存储内核image的起始地址和长度
typedef struct image_info {ulong start, end;/* blob 开始和结束位置*/ulong image_start, image_len;/* 镜像起始地址(包括 blob)和长度 */ ulong load;/* 系统镜像加载地址*/uint8_t comp, type, os;/* 镜像压缩、类型,OS 类型 */uint8_t arch;/* CPU 架构 */} image_info_t;
i.MX8MP的启动参数和启动脚本如下,
"scriptaddr=0x43500000 " "kernel_addr_r=" __stringify(CONFIG_LOADADDR) "