Uboot启动分析

阅读: 评论:0

Uboot启动分析

Uboot启动分析

总目录

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");
}

bootdelay_process

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;
}

autoboot_command

运行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 */
}

run_command_list

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的,哪种类型,加载内存中的哪个位置, 入口点在内存的那个位置以及映象名是什么。

cmd_process

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
);

do_booti

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) "" "bsp_script=boot.scr" "image=Image" "splashimage=0x50000000" "console=ttymxc1,115200" "fdt_addr_r=0x43000000"			"fdt_addr=0x43000000"			"boot_fdt=try" "fdt_high=0xffffffffffffffff"		"boot_fit=no" "fdtfile=" CONFIG_DEFAULT_FDT_FILE "" "bootm_size=0x10000000" "mmcdev="__stringify(CONFIG_SYS_MMC_ENV_DEV)"" "mmcpart=" __stringify(CONFIG_SYS_MMC_IMG_LOAD_PART) "" "mmcroot=" CONFIG_MMCROOT " rootwait rw" "mmcautodetect=yes" "mmcargs=setenv bootargs ${jh_clk} console=${console} root=${mmcroot} " "loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${bsp_script};" "bootscript=echo Running bootscript from mmc ...; " "source" "loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}" "loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr_r} ${fdtfile}" "mmcboot=echo Booting from mmc ...; " "run mmcargs; " "if test ${boot_fit} = yes || test ${boot_fit} = try; then " "bootm ${loadaddr}; " "else " "if run loadfdt; then " "booti ${loadaddr} - ${fdt_addr_r}; " "else " "echo WARN: Cannot load the DT; " "fi; " "fi;" "netargs=setenv bootargs ${jh_clk} console=${console} " "root=/dev/nfs " "ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp" "netboot=echo Booting from net ...; " "run netargs;  " "if test ${ip_dyn} = yes; then " "setenv get_cmd dhcp; " "else " "setenv get_cmd tftp; " "fi; " "${get_cmd} ${loadaddr} ${image}; " "if test ${boot_fit} = yes || test ${boot_fit} = try; then " "bootm ${loadaddr}; " "else " "if ${get_cmd} ${fdt_addr_r} ${fdtfile}; then " "booti ${loadaddr} - ${fdt_addr_r}; " "else " "echo WARN: Cannot load the DT; " "fi; " "fi;" "bsp_bootcmd=echo Running BSP bootcmd ...; " "mmc dev ${mmcdev}; if mmc rescan; then " "if run loadbootscript; then " "run bootscript; " "else " "if run loadimage; then " "run mmcboot; " "else run netboot; " "fi; " "fi; " "fi;"

emmc启动脚本分析:

启动的第一步,加载kernel image,然后执行mmcboot函数

"if run loadimage; then " "run mmcboot; " 

loadimage和loadfdt是从emmc的指定位置读取image 和dtb,image地址存入变量loadaddr,dtb地址存入变量fdt_addr_r

loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}
loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr_r} ${fdtfile}

mmcboot函数的定义如下,由于定义了boot_fit=no,所以会先loadfdt从emmc加载dtb,然后booti

${loadaddr} - ${fdt_addr_r}启动kenel image。

"mmcboot=echo Booting from mmc ...; " "run mmcargs; " "if test ${boot_fit} = yes || test ${boot_fit} = try; then " "bootm ${loadaddr}; " "else " "if run loadfdt; then " "booti ${loadaddr} - ${fdt_addr_r}; " "else " "echo WARN: Cannot load the DT; " "fi; " "fi;" 

等效为d0_booti loadaddr fdt_addr_r

booti

int do_booti(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{int ret;argc--; argv++;if (booti_start(cmdtp, flag, argc, argv, &images))return 1;//关闭中断bootm_disable_interrupts();#设置引导的os类型images.os.os = IH_OS_LINUX;#设置引导os的cpu架构images.os.arch = IH_ARCH_ARM64;ret = do_bootm_states(cmdtp, flag, argc, argv,BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |BOOTM_STATE_OS_GO,&images, 1);return ret;
}

booti_start

static int booti_start(struct cmd_tbl *cmdtp, int flag, int argc,char *const argv[], bootm_headers_t *images)
{int ret;ulong ld;ulong relocated_addr;ulong image_size;uint8_t *temp;ulong dest;ulong dest_end;unsigned long comp_len;unsigned long decomp_len;int ctype;//调用函数 do_bootm_states,执行 BOOTM_STATE_START 阶段ret = do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START,images, 1);//设置 images 的 ep 成员变量,也就是系统镜像的入口点,使用 booti命令启动系统的时候就会设置//系统在 DRAM 中的存储位置,这个存储位置就是系统镜像的入口点,因 此 images->epif (!argc) {ld = image_load_addr;debug("*  kernel: default image load address = 0x%08lxn",image_load_addr);} else {ld = hextoul(argv[0], NULL);debug("*  kernel: cmdline image address = 0x%08lxn", ld);}temp = map_sysmem(ld, 0);ctype = image_decomp_type(temp, 2);if (ctype > 0) {dest = env_get_ulong("kernel_comp_addr_r", 16, 0);comp_len = env_get_ulong("kernel_comp_size", 16, 0);if (!dest || !comp_len) {puts("kernel_comp_addr_r or kernel_comp_size is not provided!n");return -EINVAL;}if (dest < gd->ram_base || dest > gd->ram_top) {puts("kernel_comp_addr_r is outside of DRAM range!n");return -EINVAL;}debug("kernel image compression type %d size = 0x%08lx address = 0x%08lxn",ctype, comp_len, (ulong)dest);decomp_len = comp_len * 10;//解压内核ret = image_decomp(ctype, 0, ld, IH_TYPE_KERNEL,(void *)dest, (void *)ld, comp_len,decomp_len, &dest_end);if (ret)return ret;/* dest_end contains the uncompressed Image size */memmove((void *) ld, (void *)dest, dest_end);}unmap_sysmem((void *)ld);//调用 bootz_setup 函数,此函数会判断当前的系统镜像文件是否为 Linux 的镜像文件,并且会打印出镜像相关信息,ret = booti_setup(ld, &relocated_addr, &image_size, false);if (ret != 0)return 1;/* Handle BOOTM_STATE_LOADOS */if (relocated_addr != ld) {printf("Moving Image from 0x%lx to 0x%lx, end=%lxn", ld,relocated_addr, relocated_addr + image_size);memmove((void *)relocated_addr, (void *)ld, image_size);}//打印内核重定位信息images->ep = relocated_addr;images->os.start = relocated_addr;images-&d = relocated_addr + image_size;lmb_reserve(&images->lmb, images->ep, le32_to_cpu(image_size));//调用函数 bootm_find_images 查找 ramdisk 和设备树(dtb)文件,但是我们没有用到 ramdisk,因此此函数在这里仅仅用于查找设备树(dtb)文件,此函数稍后也会讲解if (bootm_find_images(flag, argc, argv, relocated_addr, image_size))return 1;return 0;
}
booti_setup
int booti_setup(ulong image, ulong *relocated_addr, ulong *size,bool force_reloc)
{struct Image_header *ih;uint64_t dst;uint64_t image_size, text_offset;*relocated_addr = image;//从传递进来的参数 image(也就是系统镜像首地址)中获取image头。Image头结构体为 image_headerih = (struct Image_header *)map_sysmem(image, 0);//判断 image 是否为ARM的 Linux 系统镜像,如果不是的话就直接返回if (ih->magic != le32_to_cpu(LINUX_ARM64_IMAGE_MAGIC)) {puts("Bad Linux ARM64 Image magic!n");return 1;}if (ih->image_size == 0) {puts("Image lacks image_size field, assuming 16MiBn");image_size = 16 << 20;text_offset = 0x80000;} else {//初始化函数 booti_setup 的参数 start 和 endimage_size = le64_to_cpu(ih->image_size);text_offset = le64_to_cpu(ih->text_offset);}*size = image_size;/** If bit 3 of the flags field is set, the 2MB aligned base of the* kernel image can be anywhere in physical memory, so respect* images->ep.  Otherwise, relocate the image to the base of RAM* since memory below it is not accessible via the linear mapping.*/if (!force_reloc && (le64_to_cpu(ih->flags) & BIT(3)))dst = image - text_offset;elsedst = gd->bd->bi_dram[0].start;*relocated_addr = ALIGN(dst, SZ_2M) + text_offset;unmap_sysmem(ih);return 0;
}
bootm_find_images

bootm_find_images函数的任务是查找 ramdisk,查找设备树(dtb)文件,找到以后就将设备树的起始地址和长度分别写到 images 的 ft_addr 和 ft_len 成员变量中。

int bootm_find_images(int flag, int argc, char *const argv[], ulong start,ulong size)
{int ret;/* find ramdisk */ret = boot_get_ramdisk(argc, argv, &images, IH_INITRD_ARCH,&images.rd_start, &images.rd_end);if (ret) {puts("Ramdisk image is corrupt or invalidn");return 1;}/* check if ramdisk overlaps OS image */if (images.rd_start && (((ulong)images.rd_start >= start &&(ulong)images.rd_start < start + size) ||((ulong)images.rd_end > start &&(ulong)images.rd_end <= start + size) ||((ulong)images.rd_start < start &&(ulong)images.rd_end >= start + size))) {printf("ERROR: RD image overlaps OS image (OS=0x%lx..0x%lx)n",start, start + size);return 1;}#if IMAGE_ENABLE_OF_LIBFDT/* find flattened device tree */ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, &images,&images.ft_addr, &images.ft_len);if (ret) {puts("Could not find a valid device treen");return 1;}/* check if FDT overlaps OS image */if (images.ft_addr &&(((ulong)images.ft_addr >= start &&(ulong)images.ft_addr <= start + size) ||((ulong)images.ft_addr + images.ft_len >= start &&(ulong)images.ft_addr + images.ft_len <= start + size))) {printf("ERROR: FDT image overlaps OS image (OS=0x%lx..0x%lx)n",start, start + size);return 1;}if (CONFIG_IS_ENABLED(CMD_FDT))//映射为有效地址set_working_fdt_addr(map_to_sysmem(images.ft_addr));
#endif#if IMAGE_ENABLE_FIT/* find all of the loadables *///fit images模式ret = boot_get_loadable(argc, argv, &images, IH_ARCH_DEFAULT,NULL, NULL);if (ret) {printf("Loadable(s) is corrupt or invalidn");return 1;}
#endifreturn 0;
}

本文发布于:2024-01-28 20:40:06,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170644560910140.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:Uboot
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23