Bootm启动流程分析
Bootm启动流程分析
- 如何引导内核
- uboot启动命令
- 内核镜像介绍
- 内核启动前提
- Bootm命令详解
- Bootm命令格式
- do_bootm
- do_bootm_subcommand
- images全局变量
- do_bootmd_states
- bootm_start
- bootm_find_os
- boot_get_kernel
- bootm_find_other
- bootm_load_os
- boot_ramdisk_high
- boot_relocate_fdt
- bootm_os_get_boot_func
- do_bootm_linux
- boot_prep_linux
- params全局变量
- boot_jump_linux
- 当前images中的值
- 优化启动时间
如何引导内核
uboot启动命令
- 从Flash/TFTP加载uImage(zImage、Image),根文件系统、设备树dtb到SDRAM
mmc read image_addr part_off part_size
mmc read ramdisk_addr part_off part_size
mmc read dtb_addr part_off part_size
tftp uImage image_addr
tftp ramdisk ramdisk_addr
tftp dtb dtb_addr
- 执行bootm(bootz、booti)命令,由内核接管控制权
#bootm/bootz/booti [image_addr] [ramdisk_addr] [dtb_addr]
#任何一项都可以没有,如果没有,用 “-’代替
用法1:bootm #使用默认的镜像地址启动
用法2:bootm image_addr #指定镜像地址启动。一般是uImage
用法3:bootm image_addr - dtb_addr #指定设备树地址
内核镜像介绍
# 编译内核时的链接过程
...LD vmlinuxSORTEX vmlinuxSYSMAP System.mapOBJCOPY arch/arm/boot/ImageKernel: arch/arm/boot/Image is readySHIPPED arch/arm/boot/compressed/hyp-stub.SSHIPPED arch/arm/boot/compressed/fdt_rw.cSHIPPED arch/arm/boot/compressed/fdt.hSHIPPED arch/arm/boot/compressed/libfdt.hSHIPPED arch/arm/boot/compressed/libfdt_internal.hSHIPPED arch/arm/boot/compressed/fdt_ro.cSHIPPED arch/arm/boot/compressed/fdt_wip.cSHIPPED arch/arm/boot/compressed/fdt.cSHIPPED arch/arm/boot/compressed/lib1funcs.SSHIPPED arch/arm/boot/compressed/ashldi3.SSHIPPED arch/arm/boot/compressed/bswapsdi2.SLDS arch/arm/boot/compressed/vmlinux.ldsAS arch/arm/boot/compressed/head.oGZIP arch/arm/boot/compressed/piggy_dataCC arch/arm/boot/compressed/misc.oCC arch/arm/boot/compressed/decompress.oCC arch/arm/boot/compressed/string.oAS arch/arm/boot/compressed/hyp-stub.oCC arch/arm/boot/compressed/fdt_rw.oCC arch/arm/boot/compressed/fdt_ro.oCC arch/arm/boot/compressed/fdt_wip.oCC arch/arm/boot/compressed/fdt.oCC arch/arm/boot/compressed/atags_to_fdt.oAS arch/arm/boot/compressed/lib1funcs.oAS arch/arm/boot/compressed/ashldi3.oAS arch/arm/boot/compressed/bswapsdi2.oAS arch/arm/boot/compressed/piggy.oLD arch/arm/boot/compressed/vmlinuxOBJCOPY arch/arm/boot/zImageKernel: arch/arm/boot/zImage is ready
# 第一步生成未经过压缩的vmlinux、Image
## vmlinux ---OBJCOPY---> arch/arm/boot/Image
# 第二步生成压缩的vmlinux、zImage
## arch/arm/boot/compressed/vmlinux ---OBJCOPY ---> arch/arm/boot/zImage
vmlinux:Linux内核编译生成原始内核文件,ELF格式,该映像可用于定位内核问题(readelf -s vmlinux
),但不能直接引导Linux系统启动。
Image :使用objcopy处理vmlinux后( 去除其中的符号和重定位信息等 )生成的二进制内核映像。该映像未压缩,可直接引导Linux系统启动。
zImage:普通的压缩内核映像文件,使用gzip压缩Image后生成的Linux内核映像。
uImage:使用工具mkimage对普通的压缩内核映像文件(zImage)加工而得。它是uboot专用的映像文件,它是在zImage之前加上一个长度为64字节的头,说明这个内核的版本、加载位置、生成时间、大小等信息。
# path:u-boot-2019.04/tool/mkimage
# linux-4.19.91/scripts/mkuboot.sh
# 帮助文档 u-boot-2019.04/docmkimage.1
Usage: ./mkimage -l image-l ==> list image header information./mkimage [-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image-A ==> set architecture to 'arch' #指定CPU的体系结构 arm x86 -O ==> set operating system to 'os' #指定操作系统类型 linux openbsd-T ==> set image type to 'type' #指定映象类型 kernel、ramdisk-C ==> set compression type 'comp' #指定映象压缩方式 none gzip -a ==> set load address to 'addr' (hex) #指定映象在内存中的加载地址-e ==> set entry point to 'ep' (hex) #指定映象运行的入口点地址,addr+0x40(头部长度)-n ==> set image name to 'name' #指定映象名-d ==> use image data from 'datafile' #指定制作映象的源文件-x ==> set XIP (execute in place)workspace@: ./mkuboot.sh -A arm -O linux -T kernel -C none -a 0x8000 -e 0x8000 -n 'my_kernel' -d ../arch/arm/boot/zImage uImage
Image Name: my_kernel
Created: Mon Mar 14 14:46:14 2022
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1882944 Bytes = 1838.81 kB = 1.80 MB
Load Address: 00008000
Entry Point: 00008000
image header
mkuboot.sh -l uImage
hexdump -C uImage | less
// path:u-boot-2019.04/include/image.h
/** Legacy format image header,* all data in network byte order (aka natural aka bigendian).*/
typedef struct image_header {__be32 ih_magic; /* Image Header Magic Number(镜像头部幻数为 #define IH_MAGIC 0x27051956) */__be32 ih_hcrc; /* Image Header CRC Checksum(镜像头部CRC校验码) */ __be32 ih_time; /* Image Creation Timestamp(镜像创建时间戳) */__be32 ih_size; /* Image Data Size(镜像数据大小(不算头部))1882944 Bytes */__be32 ih_load; /* Data Load Address(镜像数据将要载入的内存地址)0x8000 */__be32 ih_ep; /* Entry Point Address(镜像入口地址)0x8000 */__be32 ih_dcrc; /* Image Data CRC Checksum(镜像数据CRC校验码) */ uint8_t ih_os; /* Operating System(操作系统类型)05 IH_OS_LINUX */uint8_t ih_arch; /* CPU architecture(CPU架构)02 IH_ARCH_ARM */uint8_t ih_type; /* Image Type(镜像类型)02 IH_TYPE_KERNEL */uint8_t ih_comp; /* Compression Type(压缩类型)00 IH_COMP_NONE */uint8_t ih_name[IH_NMLEN]; /* Image Name(镜像名字ih_name,共32字节 #define IH_NMLEN 32 'my_kernel') */
} image_header_t;
内核启动前提
参考linux-4.19.148/Documentation/arm/Booting和linux-4.19.148/Documentation/arm64/booting.txt
arm架构处理器对linux内核启动之前环境的需求
cpu 寄存器设置
ARM:
R0 = 0
R1 = 板级 id
R2 = 启动参数在内存中的起始地址
ARM64:
x0 = dtb在系统内存的物理地址
x1 = 0 保留给以后使用
x2 = 0 保留给以后使用
x3 = 0 保留给以后使用cpu 模式
禁止所有中断
ARM:必须为SVC(超级用户)模式
ARM64:处于EL2或者非安全模式的EL1模式中缓存、MMU
关闭 MMU
指令缓存可以开启或者关闭
数据缓存必须关闭并且不能包含任何脏数据DMA 设备应当停止工作
bootloader需要跳转到内核镜像的第一条指令处
ARM64下系统寄存器设置
内核镜像将要进入的异常级别(EL)前,必须在其更高的异常级别(EL)进行初始化,以防止在一个未知的状态执行。
Bootm命令详解
bootm要做的事情
a. 读取头部,把内核拷贝到合适的地方(bootm_headers_t images)
b. 将启动参数给内核准备好,并告诉内核参数的首地址
c. 设置cpu寄存器,禁止中断,关闭MMU和cache
d. 跳转到内核的入口地址,kernel开始运行
Bootm命令格式
#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, \_help, _comp) \{ #_name, _maxargs, _rep, 0 ? _cmd : NULL, _usage, \_CMD_HELP(_help) _CMD_COMPLETE(_comp) }
//name:命令名,非字符串,但在U_BOOT_CMD中用“#”符号转化为字符串
//maxargs:命令的最大参数个数
//repeatable:是否自动重复(按Enter键是否会重复执行)
//command:该命令对应的响应函数指针
//usage:简短的使用说明(字符串)
//help:较详细的使用说明(字符串)U_BOOT_CMD(bootm, CONFIG_SYS_MAXARGS, 1, do_bootm,"boot application image from memory", bootm_help_text
);
Usage:
bootm [addr [arg ...]]- boot application image stored in memorypassing arguments 'arg ...'; when booting a Linux kernel,'arg' can be the address of an initrd imageWhen booting a Linux kernel which requires a flat device-treea third argument is required which is the address of thedevice-tree blob. To boot that kernel without an initrd image,use a '-' for the second argument. If you do not pass a thirda bd_info struct will be passed insteadSub-commands to do part of the bootm sequence. The sub-commands must be
issued in the order below (it's ok to not issue all sub-commands):start [addr [arg ...]]loados - load OS imageramdisk - relocate initrd, set env initrd_start/initrd_endfdt - relocate flat device treecmdline - OS specific command line processing/setupbdt - OS specific bd_t processingprep - OS specific prep before relocation or gogo - start OS#example
bootm # 使用默认的镜像地址启动
bootm image_addr # 指定镜像地址
bootm image_addr - dtb_addr # 同时指定设备树dtb地址
bootm image_addr ramdisk_addr dtb_addr # 指定ramdisk、dtb地址
bootm start [image_addr] [ramdisk_addr] [dtb_addr] # 找到镜像
bootm loados # 加载镜像
bootm ramdisk # 重新定位initrd,设置环境变量initrd_start/initrd_end
bootm fdt # 重新定位设备树
bootm prep # 跳转内核前的处理,设置启动参数TAGS
bootm go # 跳转到内核入口地址
do_bootm
int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{/* determine if we have a sub command */argc--; argv++;if (argc > 0) {char *endp;simple_strtoul(argv[0], &endp, 16);if ((*endp != 0) && (*endp != ':') && (*endp != '#'))/* 执行子命令 */return do_bootm_subcommand(cmdtp, flag, argc, argv);}/* 执行bootm命令的指定状态 */return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START |BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |BOOTM_STATE_LOADOS |
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGHBOOTM_STATE_RAMDISK |
#endifBOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |BOOTM_STATE_OS_GO, &images, 1);
}
do_bootm_subcommand
static cmd_tbl_t cmd_bootm_sub[] = {U_BOOT_CMD_MKENT(start, 0, 1, (void *)BOOTM_STATE_START, "", ""),U_BOOT_CMD_MKENT(loados, 0, 1, (void *)BOOTM_STATE_LOADOS, "", ""),
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGHU_BOOT_CMD_MKENT(ramdisk, 0, 1, (void *)BOOTM_STATE_RAMDISK, "", ""),
#endif
#ifdef CONFIG_OF_LIBFDTU_BOOT_CMD_MKENT(fdt, 0, 1, (void *)BOOTM_STATE_FDT, "", ""),
#endifU_BOOT_CMD_MKENT(cmdline, 0, 1, (void *)BOOTM_STATE_OS_CMDLINE, "", ""),U_BOOT_CMD_MKENT(bdt, 0, 1, (void *)BOOTM_STATE_OS_BD_T, "", ""),U_BOOT_CMD_MKENT(prep, 0, 1, (void *)BOOTM_STATE_OS_PREP, "", ""),U_BOOT_CMD_MKENT(fake, 0, 1, (void *)BOOTM_STATE_OS_FAKE_GO, "", ""),U_BOOT_CMD_MKENT(go, 0, 1, (void *)BOOTM_STATE_OS_GO, "", ""),
};static int do_bootm_subcommand(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[])
{int ret = 0;long state;cmd_tbl_t *c;c = find_cmd_tbl(argv[0], &cmd_bootm_sub[0], ARRAY_SIZE(cmd_bootm_sub));argc--; argv++;if (c) {state = (long)c->cmd;if (state == BOOTM_STATE_START)state |= BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER;} else {/* Unrecognized command */return CMD_RET_USAGE;}if (((state & BOOTM_STATE_START) != BOOTM_STATE_START) &&images.state >= state) {printf("Trying to execute a command out of order\n");return CMD_RET_USAGE;}/* 同样执行bootm命令的指定状态 */ret = do_bootm_states(cmdtp, flag, argc, argv, state, &images, 0);return ret;
}
无论是subcommand还是一般bootm命令,最终都是执行do_bootmd_states
函数,区别入参state不同
命令 | state |
---|---|
bootm [image_addr] [ramdisk_addr] [dtb_addr] | BOOTM_STATE_START | BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER | BOOTM_STATE_LOADOS | BOOTM_STATE_RAMDISK | BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO |
bootm start | BOOTM_STATE_START | BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
bootm loados | BOOTM_STATE_LOADOS |
bootm ramdisk | BOOTM_STATE_RAMDISK |
bootm fdt | BOOTM_STATE_FDT |
bootm prep | BOOTM_STATE_OS_PREP |
bootm go | BOOTM_STATE_OS_GO |
images全局变量
do_bootmd_states
函数的其中一个参数是全局images,是引导内核的一个重要变量。
/* u-boot-2019.04/common/bootm.c */
bootm_headers_t images; /* pointers to os/initrd/fdt images *//* u-boot-2019.04/include/image.h */
/** Legacy and FIT format headers used by do_bootm() and do_bootm_<os>()* routines.*/
typedef struct bootm_headers {/** Legacy os image header, if it is a multi component image* then boot_get_ramdisk() and get_fdt() will attempt to get* data from second and third component accordingly.*/image_header_t *legacy_hdr_os; /* image header pointer 镜像头指针*/image_header_t legacy_hdr_os_copy; /* header copy 镜像头部*/ulong legacy_hdr_valid; /* header是否有效 */#ifndef USE_HOSTCCimage_info_t os; /* os image info 镜像信息*/typedef struct image_info {ulong start, end; /* start/end of blob blob起始地址和结束地址*/ulong image_start, image_len; /* start of image within blob, len of image 镜像开始地址和长度*/ulong load; /* load addr for the image image的加载地址*/uint8_t comp, type, os; /* compression, type of image, os type 压缩类型、镜像类型和操作系统类型*/uint8_t arch; /* CPU architecture 架构类型*/} image_info_t;ulong ep; /* entry point of OS 入口地址*/ulong rd_start, rd_end;/* ramdisk start/end ramdisk的起始和结束地址*/char *ft_addr; /* flat dev tree address 设备树地址*/ulong ft_len; /* length of flat device tree 设备树长度*/ulong initrd_start; /* initrd内存根文件系统起始地址 */ulong initrd_end; /* initrd内存根文件系统结束地址 */ulong cmdline_start; /* cmdline命令行起始地址 */ulong cmdline_end; /* cmdline命令行结束地址 */bd_t *kbd;
#endifint verify; /* getenv("verify")[0] != 'n' 是否要对image进行验证*/#ifdef CONFIG_LMBstruct lmb lmb; /* for memory mgmt 用来进行内存管理*/
#endif
} bootm_headers_t;
do_bootmd_states
/** @param cmdtp Pointer to bootm command table entry* @param flag Command flags (CMD_FLAG_...)* @param argc Number of subcommand arguments (0 = no arguments)* @param argv Arguments* @param states Mask containing states to run (BOOTM_STATE_...)* @param images Image header information* @param boot_progress 1 to show boot progress, 0 to not do this*/
int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],int states, bootm_headers_t *images, int boot_progress)
/* 初始化image全局变量,设置bootm的内存 */
|--if (states & BOOTM_STATE_START)
| |--ret = bootm_start
/* find os 确定image的内存地址 */
|--if (!ret && (states & BOOTM_STATE_FINDOS))
| |--ret = bootm_find_os
/* find ramdisk、fdt 确定ramdisk、fdt的内存地址 */
|--if (!ret && (states & BOOTM_STATE_FINDOTHER))
| |--ret = bootm_find_other(cmdtp, flag, argc, argv);
/* Load the OS 加载image */
|--if (!ret && (states & BOOTM_STATE_LOADOS))
| |--ret = bootm_load_os
/* Relocate the ramdisk 重定位ramdisk */
|--if (!ret && (states & BOOTM_STATE_RAMDISK))
| |--ret = boot_ramdisk_high
/* Relocate the fdt 为设备树中的memory reserve预留内存,重定位fdt */
|--if (!ret && (states & BOOTM_STATE_FDT))
| |--boot_fdt_add_mem_rsv_regions
| |--ret = boot_relocate_fdt
/* From now on, we need the OS boot function */
/* get boot_fn 获取启动函数 */
|--boot_fn = bootm_os_get_boot_func(images->os.os)
/* boot_fn-BOOTM_STATE_OS_PREP 设置dtb或者atags */
|--if (!ret && (states & BOOTM_STATE_OS_PREP))
| |--ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);
/* boot_fn-BOOTM_STATE_OS_GO 禁止中断,关闭cache,跳转到内核入口地址*/
|--if (!ret && (states & BOOTM_STATE_OS_GO))
| |--ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,images, boot_fn);
| | |--boot_fn(state, argc, argv, images);
bootm_start
初始化images全局变量
根据环境变量verify设置images.verify = 0
根据环境变量bootm_low和bootm_size设置images.lmb
setenv bootm_low = 0x0;
setenv bootm_size = 0x02000000
images.lmb.memory.region[0].base = 0x0
images.lmb.memory.region[0].size = 0x02000000
images.lmb.reserved.region[0].base = 0x0
images.lmb.reserved.region[0].size = 0x0
/* 若需要重定位fdt和ramdisk,则从该内存中进行申请 */
lmb->memory.region[0].base = bootm_low;
lmb->memory.region[0].size = bootm_size
lmb->memory.cnt = 1;
lmb->memory.size = 0;/* nt98566上未设置reserved.region */
lmb->reserved.region[0].base = sp - 4096;
lmb->reserved.region[0].size = gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - base;
lmb->reserved.cnt = 1;
lmb->reserved.size = 0;
static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[])
|--memset((void *)&images, 0, sizeof(images));
|--images.verify = getenv_yesno("verify");
/* 若定义了CONFIG_LMB,设置images.lmb,用于内存管理,否则为空函数 */
|--boot_start_lmb(&images);
|--images.state = BOOTM_STATE_START;
bootm_find_os
bootm 0x5800000 - 0x5c00000
- 设置mages.os.image_start = 0x5800000 + 0x40 = 0x5800040
- 设置images.os.image_len = 0x207c28
- 将0x5800000上的0x40大小的头部拷贝给images->legacy_hdr_os_copy
- 设置images->legacy_hdr_os = 0x5800000
- 设置images->legacy_hdr_valid = 1;
- 设置images.os.type = image_get_type(os_hdr) = IH_TYPE_KERNEL
- 设置images.os.comp = image_get_comp(os_hdr) = IH_COMP_NONE
- 设置images.os.os = image_get_os(os_hdr) = IH_OS_LINUX
- 设置images.os.end = image_get_image_end(os_hdr) = 0x5800000 + 0x207c28+ 0x40 = 0x5a07c68
- 设置images.os.load = image_get_load(os_hdr) = 0x8000
- 设置images.os.arch = image_get_arch(os_hdr) = IH_ARCH_ARM
- 设置images.ep = image_get_ep(&images.legacy_hdr_os_copy) = 0x8000
- 设置images.os.start = 0x5800000
static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[])
|--const void *os_hdr;
/* get kernel image header, start address and length */
|--os_hdr = boot_get_kernel(cmdtp, flag, argc, argv,&images, &images.os.image_start, &images.os.image_len);
/* get image parameters */
|--switch (genimg_get_format(os_hdr)) {| |--case IMAGE_FORMAT_LEGACY:
| | |--images.os.type = image_get_type(os_hdr);
| | |--images.os.comp = image_get_comp(os_hdr);
| | |--images.os.os = image_get_os(os_hdr);
| | |--images.os.end = image_get_image_end(os_hdr);
| | |--images.os.load = image_get_load(os_hdr);
| | |--images.os.arch = image_get_arch(os_hdr);
|--}
|--if (images.legacy_hdr_valid)
| |--images.ep = image_get_ep(&images.legacy_hdr_os_copy);
|--images.os.start = map_to_sysmem(os_hdr);
boot_get_kernel
static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[], bootm_headers_t *images,ulong *os_data, ulong *os_len)
|--image_header_t *hdr;
/* img_addr = simple_strtoul(argv[0], NULL, 16) */
|--ulong img_addr = genimg_get_kernel_addr_fit(argc < 1 ? NULL : argv[0],&fit_uname_config,&fit_uname_kernel);
|--const void *buf = map_sysmem(img_addr, 0);
|--switch (genimg_get_format(buf)){| |--case IMAGE_FORMAT_LEGACY:
/* verify legacy format kernel image */
| | |--hdr = image_get_kernel(img_addr, images->verify);
/* get os_data and os_len */
| | |--switch (image_get_type(hdr)) {| | | |--case IH_TYPE_KERNEL:case IH_TYPE_KERNEL_NOLOAD:*os_data = image_get_data(hdr);*os_len = image_get_data_size(hdr);
| | |--}
/* copy image header to allow for image overwrites during kernel decompression.*/
| | |--memmove(&images->legacy_hdr_os_copy, hdr,sizeof(image_header_t));
| | |--images->legacy_hdr_os = hdr;
| | |--images->legacy_hdr_valid = 1;
|--}
|--return buf;
bootm_find_other
bootm 0x5800000 - 0x5c00000
- 设置images.rd_start = 0
- 设置images.rd_end = 0
- 设置images.ft_addr = 0x5c00000
- 设置images.ft_len= 0x45ea
static int bootm_find_other(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[])
|--return bootm_find_images(flag, argc, argv);
/* find ramdisk */
| |--ret = boot_get_ramdisk(argc, argv, &images, IH_INITRD_ARCH,&images.rd_start, &images.rd_end);
/* find flattened device tree */
| |--ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, &images,&images.ft_addr, &images.ft_len);
| |--set_working_fdt_addr((ulong)images.ft_addr);
| | |--setenv_hex("fdtaddr", addr);
bootm_load_os
解压内核镜像,这里指的是制作uImage时的压缩类型,当前为IH_COMP_NONE
若images.os.load == mages.os.image_start,即bootm传入的image_addr+0x40等于制作uImage时指定的load地址,无需进行memmove
若images.os.load != mages.os.image_start,则将mages.os.image_start上的image镜像拷贝到images.os.load地址上,长度为images.os.image_len
当前images.os.load=0x8000, mages.os.image_start=0x5800040,需要进行镜像拷贝
if (!ret && (states & BOOTM_STATE_LOADOS))
/* 在准备加载/引导时禁用中断 */
|--ulong iflag = bootm_disable_interrupts()
|--ret = bootm_load_os(images, &load_end, 0);static int bootm_load_os(bootm_headers_t *images, unsigned long *load_end,int boot_progress)
|--int err = image_decomp(os.comp, load, os.image_start, os.type,load_buf, image_buf, image_len,CONFIG_SYS_BOOTM_LEN, &load_end);
/** images.os.load=0x8000, mages.os.image_start=0x5800040* images.os.image_len = 0x207c28, CONFIG_SYS_BOOTM_LEN=0x1900000*/
| |--if (load == os.image_start)
| | |--break
| |--if (image_len <= CONFIG_SYS_BOOTM_LEN)
/* 将0x5800040上的镜像拷贝到0x8000,拷贝长度为0x207c28 */
| | |--memmove_wd(load_buf, image_buf, image_len, CHUNKSZ); /* 刷新load范围的d-cache/统一缓存 */
|--flush_cache(load, ALIGN(load_end - load, ARCH_DMA_MINALIGN));
/* 将images->os.load到oad_end的这片内存保留 */
|--lmb_reserve(&images->lmb, images->os.load, (load_end -images->os.load));
boot_ramdisk_high
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
ulong rd_len = images->rd_end - images->rd_start;
int ret = boot_ramdisk_high(&images->lmb, images->rd_start,rd_len, &images->initrd_start, &images->initrd_end);
if (!ret) {setenv_hex("initrd_start", images->initrd_start);setenv_hex("initrd_end", images->initrd_end);}
#endifint boot_ramdisk_high(struct lmb *lmb, ulong rd_data, ulong rd_len,ulong *initrd_start, ulong *initrd_end)
|--if ((s = getenv("initrd_high")) != NULL)
| |--ulong initrd_high = simple_strtoul(s, NULL, 16);
| |--if (initrd_high == ~0)
| | |--initrd_copy_to_ram = 0;
|--else
| |--initrd_high = getenv_bootm_mapsize() + getenv_bootm_low();|--if (rd_data)
| |--if (!initrd_copy_to_ram)
| |--*initrd_start = rd_data;
| |--*initrd_end = rd_data + rd_len;
| |--lmb_reserve(lmb, rd_data, rd_len);
|--else
| |--if (initrd_high)
| | |--*initrd_start = (ulong)lmb_alloc_base(lmb,rd_len, 0x1000, initrd_high);
| |--else
| | |--*initrd_start = (ulong)lmb_alloc(lmb, rd_len,0x1000);
| |--*initrd_end = *initrd_start + rd_len;
| |--memmove_wd((void *)*initrd_start,(void *)rd_data, rd_len, CHUNKSZ);
|--else
| |--*initrd_start = 0;
| |--*initrd_end = 0;
bootm指定ramdisk_addr
setenv initrd_high 不等于~0
从lmb.memory.region[0]中申请内存,且内存不高于initrd_high,并拷贝镜像
images->initrd_start = (ulong)lmb_alloc_base(lmb, rd_len, 0x1000, initrd_high);
images->initrd_end = images->initrd_start + images->rd_end - images->rd_start;
memmove_wd(void *)*initrd_start, (void *)rd_data, rd_len, CHUNKSZ)
setenv initrd_high 0xffffffff,不进行reloacate,保留这块内存
images->initrd_start = images->rd_start
images->initrd_end = images->rd_end
lmb_reserve(lmb, rd_data, rd_len)
setenv initrd_high 0
从lmb.memory.region[0]中任意位置申请内存,并拷贝镜像
images->initrd_start = (ulong)lmb_alloc(lmb, rd_len, 0x1000);
mages->initrd_end = images->initrd_start + images->rd_end - images->rd_start;
memmove_wd(void *)*initrd_start, (void *)rd_data, rd_len, CHUNKSZ)
没有环境变量initrd_high
initrd_high = bootm_mapsize + bootm_low
nt98566上,bootm未指定ramdisk_addr
images->initrd_start = 0
images->initrd_end = 0
boot_relocate_fdt
/* 只有执行bootm start、bootm fdt子命令才生效 */
#if IMAGE_ENABLE_OF_LIBFDT && defined(CONFIG_LMB)if (!ret && (states & BOOTM_STATE_FDT)) {/* 为设备树中的memory reserve预留内存 */boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr);ret = boot_relocate_fdt(&images->lmb, &images->ft_addr,&images->ft_len);}
#endifint boot_relocate_fdt(struct lmb *lmb, char **of_flat_tree, ulong *of_size)
|--void *fdt_blob = *of_flat_tree;
|--ulong of_len = *of_size + CONFIG_SYS_FDT_PAD;
|--char *fdt_high = getenv("fdt_high")
|--if (fdt_high)
| |--void *desired_addr = (void *)simple_strtoul(fdt_high, NULL, 16);
| |--if (((ulong) desired_addr) == ~0UL)
| | |--void *of_start = fdt_blob;
| | |--lmb_reserve(lmb, (ulong)of_start, of_len);
| | |--int disable_relocation = 1
| |--else if (desired_addr)
| | |--of_start = (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000,(ulong)desired_addr);
| |--else
| | |--of_start = (void *)(ulong) lmb_alloc(lmb, of_len, 0x1000);
|--else
| |--of_start = (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000,getenv_bootm_mapsize() + getenv_bootm_low());|--if (disable_relocation)
/* 设置设备树,并进行镜像拷贝 */
| |--fdt_set_totalsize(of_start, of_len);
|--else
| |--int err = fdt_open_into(fdt_blob, of_start, of_len)|--*of_flat_tree = of_start;
|--*of_size = of_len;
|--set_working_fdt_addr((ulong)*of_flat_tree);
| |--setenv_hex("fdtaddr", addr);
setenv fdt_high 不等于~0
从lmb.memory.region[0]中申请内存,且内存不高于initrd_high,并拷贝镜像
images->ft_addr = (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000, (ulong)initrd_high);
images->ft_len += CONFIG_SYS_FDT_PAD
memmove
setenv fdt_high 0xffffffff, 不进行reloacate,保留这块内存
images->ft_addr 即bootm 传入的dtb_addr
images->ft_len += CONFIG_SYS_FDT_PAD
lmb_reserve
setenv fdt_high 0
从lmb.memory.region[0]中任意位置申请内存
mages->ft_addr = (void *)(ulong) lmb_alloc(lmb, of_len, 0x1000);
images->ft_len += CONFIG_SYS_FDT_PAD
没有环境变量fdt_high
fdt_high = bootm_mapsize + bootm_low
bootm_os_get_boot_func
- 根据images->os.os获取对应的boot_os_fn,当前images->os.os为IH_OS_LINUX,boot_fn为do_bootm_linux
images->os.os = IH_OS_LINUX
boot_fn = bootm_os_get_boot_func(images->os.os)static boot_os_fn *boot_os[] = {[IH_OS_U_BOOT] = do_bootm_standalone,
#ifdef CONFIG_BOOTM_LINUX[IH_OS_LINUX] = do_bootm_linux,
#endif/* ... */
};boot_os_fn *bootm_os_get_boot_func(int os)
|--return boot_os[os];
do_bootm_linux
- 执行boot_fn(BOOTM_STATE_OS_PREP,argc, argv, images
- 执行boot_fn(BOOTM_STATE_OS_GO, argc, argv, images)
if (!ret && (states & BOOTM_STATE_OS_PREP))
|--ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);
if (!ret && (states & BOOTM_STATE_OS_GO))
|--ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,images, boot_fn);
| |--boot_fn(state, argc, argv, images);int do_bootm_linux(int flag, int argc, char * const argv[],bootm_headers_t *images)
|--if (flag & BOOTM_STATE_OS_PREP)
| |--boot_prep_linux(images);
| |--return 0;
|--boot_prep_linux(images);
|--boot_jump_linux(images, flag);
|--return 0;
boot_prep_linux
设置FDT
为设备树中的reserved-memory预留内存,nt98566中为cma(连续的内存分配器)预留内存,地址为0x01a00000,长度为0
重定位设备树,见boot_relocate_fdt小节
setenv fdt_high 0xffffffff
bootm 0x5800000 - 0x5c00000
mages->ft_addr=0x5c00000
images->ft_len = 0x45ea /* 区别bootm fdt命令,正常启动不修改其长度(可能是代码bug,此变量在后续启动流程中无用) */
修改设备树,取代传统的TAG
设置ATAGS,从gd->bd->bi_boot_params开始设置要传给内核的TAG信息
static void boot_prep_linux(bootm_headers_t *images)
|--char *commandline = getenv("bootargs");
/* USE FDT,将uboot要向kernel传递的信息修改到设备树, 并将R2寄存器指向该设备树地址*/
|--if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
| |--image_setup_linux(images)
/* 为设备树中的memory reserve预留内存 */
| | |--boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree);
/* 从lmb.memory.region[0]中申请内存以存放bootargs */
| | |--boot_get_cmdline(lmb, &images->cmdline_start, &images->cmdline_end);
/* 重定位设备树 */
| | |--ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size);
/* 修改设备树,取代传统的TAG */
| | |--ret = image_setup_libfdt(images, *of_flat_tree, of_size, lmb);
| | | |--fdt_root(blob) /* 修改序列号serial-number */
| | | |--fdt_chosen(blob) /* 传递bootargs到chosen节点 */
| | | |--arch_fixup_fdt(blob) /* gd->bd->bi_dram[bank]修改设备树中的memory节点 */
| | | |--fdt_fixup_ethernet(blob); /* 修改设备树中的mac-address */
| | | |--ft_board_setup(blob, gd->bd); /* 空 */
| | | |--ft_system_setup(blob, gd->bd); /* 空 */
| | | |--fdt_shrink_to_minimum(blob, 0); /* 压缩设备树体积 */
| | | |--lmb_reserve(lmb, (ulong)blob, of_size); /* 重新为设备树保留内存 */
| | | |--fdt_initrd(blob, *initrd_start, *initrd_end); /* 设置chosen中的linux,initrd-start和linux,initrd-end *//* USE ATAGS */
/* tag类型的结构体指针params最初指向bd->bi_boot_params,连续向上偏移hdr.size,直到setup_end_tag,同设备树传递的信息,只是将这些信息放到bd->bi_boot_params地址上,并将R2寄存器指向该地址 */
|--else if (BOOTM_ENABLE_TAGS)
| |--setup_start_tag(gd->bd);
| | |--params = (struct tag *)bd->bi_boot_params;
| | |--params = tag_next (params);
| |--setup_serial_tag(¶ms);
| | |--params->u.serialnr.low = serialnr.low;
| | |--params->u.serialnr.high= serialnr.high;
| | |--params = tag_next (params);
| |--setup_commandline_tag(gd->bd, commandline);
| |--setup_revision_tag(¶ms);
| |--setup_memory_tags(gd->bd);
| |--setup_initrd_tag(gd->bd, images->initrd_start, images->initrd_end);
| |--setup_initrd_tag(gd->bd, images->rd_start, images->rd_end);
| |--setup_board_tags(¶ms);
| |--setup_end_tag(gd->bd);
params全局变量
/* u-boot-2019.04/arch/arm/lib/bootm.c */
static struct tag *params;/* arch/arm/include/asm/setup.h */
struct tag_header {u32 size;u32 tag;
};struct tag_core {u32 flags; /* bit 0 = read-only */u32 pagesize;u32 rootdev;
};struct tag_mem32 {u32 size;u32 start; /* physical start address */
};
/* ... */struct tag {struct tag_header hdr;union {struct tag_core core;struct tag_mem32 mem;struct tag_videotext videotext;struct tag_ramdisk ramdisk;struct tag_initrd initrd;struct tag_serialnr serialnr;struct tag_revision revision;struct tag_videolfb videolfb;struct tag_cmdline cmdline;/** Acorn specific*/struct tag_acorn acorn;/** DC21285 specific*/struct tag_memclk memclk;} u;
};
boot_jump_linux
禁止中断,关闭cache,关闭mmu
跳转到内核的入口地址images->ep
#ifdef CONFIG_ARM64
- CONFIG_ARMV8_SWITCH_TO_EL1:
armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0, (u64)switch_to_el1, ES_TO_AARCH64);
- 32-bit OS
armv8_switch_to_el2(0, (u64)gd->bd->bi_arch_number, (u64)images->ft_addr, 0, (u64)images->ep, ES_TO_AARCH32);
- 64-bit OS
armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0, images->ep, ES_TO_AARCH64);
#else
kernel_entry = (void (*)(int, int, uint))images->ep;
machid = gd->bd->bi_arch_number
或strict_strtoul(env_get("machid"), 16, &machid)
FDT:
r2 = (unsigned long)images->ft_addr;
ATAGS:
r2 = gd->bd->bi_boot_params;
kernel_entry(0, machid, r2);
static void boot_jump_linux(bootm_headers_t *images, int flag)/* ARM64 */
#ifdef CONFIG_ARM64
|--void (*kernel_entry)(void *fdt_addr, void *res0, void *res1, void *res2);
|--kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,void *res2))images->ep;
|--announce_and_cleanup(fake);
/* 禁止中断,关闭cache,关mmu */
| |--cleanup_before_linux();
| | |--disable_interrupts();
| | |--dcache_disable();
| | |--icache_disable();
| | |--invalidate_icache_all();
| | |--cpu_cache_initialization
/* flush cache before swtiching to EL2 */
|--do_nonsec_virt_switch();
| |--smp_kick_all_cpus();
| |--dcache_disable();#ifdef CONFIG_ARMV8_SWITCH_TO_EL1
| |--armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0,(u64)switch_to_el1, ES_TO_AARCH64);#else
| |--if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) &&(images->os.arch == IH_ARCH_ARM))
| | |--armv8_switch_to_el2(0, (u64)gd->bd->bi_arch_number,(u64)images->ft_addr, 0,(u64)images->ep,ES_TO_AARCH32);
| |--else
| | |--armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0,images->ep,ES_TO_AARCH64); #endif/* ARM */
#else
|--unsigned long machid = gd->bd->bi_arch_number;
|--void (*kernel_entry)(int zero, int arch, uint params);
|--kernel_entry = (void (*)(int, int, uint))images->ep;
|--char *s = getenv("machid");
| |--strict_strtoul(s, 16, &machid)
|--announce_and_cleanup(fake);
|--if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
| |--unsigned long r2 = (unsigned long)images->ft_addr;
|--else
| |--r2 = gd->bd->bi_boot_params;
|--kernel_entry(0, machid, r2);
当前images中的值
以NT98566平台为例:
- 片内SDRAM 128M
- setenv bootm_low = 0x0;
- setenv bootm_size = 0x02000000
- setenv bootm_mapsize = 0x02000000
- setenv fdt_high = 0xffffffff
- bootm 0x5800000 - 0x5c00000
成员 | 值 |
---|---|
image_header_t *legacy_hdr_os; | 0x5800000 |
image_header_t legacy_hdr_os_copy; | memmove(&images->legacy_hdr_os_copy, 0x5800000, sizeof(image_header_t)); |
ulong legacy_hdr_valid; | 1 |
ulong os.start | 0x5800000 |
ulong os.end | 0x5a07c68 |
ulong os.image_start | 0x5800040 |
ulong os.image_len | 0x207c28 |
ulong os.load | 0x8000 |
uint8_t os.comp | 00 IH_COMP_NONE |
uint8_t os.type | 02 IH_TYPE_KERNEL |
uint8_t os.os | 05 IH_OS_LINUX |
uint8_t os.arch | 02 IH_ARCH_ARM |
ulong ep | 0x8000 |
ulong rd_start | 0 |
ulong rd_end | 0 |
char *ft_addr | 0x5c00000 |
ulong ft_len | 0x45ea |
ulong initrd_start | 0 |
ulong initrd_end | 0 |
ulong cmdline_start | 0 |
ulong cmdline_end | 0 |
bd_t *kbd | 0 |
int verify | 0 |
int state; | BOOTM_STATE_START |
lmb.memory.cnt | 1 |
lmb.memory.size | 0 |
lmb.memory.region[0].base | 0x0 |
lmb.memory.region[0].size | 0x2000000 |
lmb.reserved.cnt | 3 |
lmb.reserved.size | 0 |
lmb.reserved.region[0].base | 0x8000 /* 为image镜像预留内存 */ |
lmb.reserved.region[0].size | 0x207c28 /* image镜像大小 */ |
lmb.reserved.region[1].base | 1a00000 /* 设备树中reserved-memory */ |
lmb.reserved.region[2].size | 0 |
lmb.reserved.region[0].base | 5c00000 /* 为设备树预留内存 */ |
lmb.reserved.region[0].size | 0x5000 /* 设备树执行fdt_shrink_to_minimum(blob, 0)后压缩过的大小 */ |
优化启动时间
- 关闭image的内容打印、加载信息
/* 例如注释以下函数 */
image_print_contents
- 若直接从flash分区加载image镜像,可以先读0x40(具体根据flash的block大小确定)字节头部,从头部获取image镜像大小,再加载image镜像,避免读取整块flash分区
sprintf(cmd, "mmc read 0x%x 0x%llx 0x%x", image_addr, part_off, 1)
run_command(cmd, 0)
hdr = (image_header_t *)image_addr;
size = image_get_data_size(hdr) + sizeof(image_header_t);
align_size = ALIGN_CEIL(size, MMC_MAX_BLOCK_LEN) / MMC_MAX_BLOCK_LEN;
sprintf(cmd, "mmc read 0x%x 0x%llx 0x%x", image_addr, part_off, align_size);
- 设置环境变量verify=n,不对image镜像的数据进行crc校验,甚至可以只对image进行magic校验
bootm_find_os
|--boot_get_kernel
| |--image_get_kernel
| | |--image_check_magic(hdr)
| | |--image_check_hcrc(hdr)
| | |--if (verify)
| | | |--image_check_dcrc(hdr)
| | |--image_check_target_arch(hdr)bootm_find_other
|--bootm_find_images
| |--boot_get_ramdisk
| | |--image_get_ramdisk
| | | |--image_check_magic
| | | |--image_check_hcrc
| | | |--if (verify)
| | | | |--image_check_dcrc(rd_hdr)
- 若制作uImage时指定的-e参数,即镜像入口地址为ep,bootm命令传入的镜像地址为image_addr,保持:ep = image_addr + 0x40(sizeof(image_header_t)),以节省image镜像的拷贝时间
bootm_load_os
|--image_decomp| |--if (load == os.image_start)
| | |--break
| |--if (image_len <= CONFIG_SYS_BOOTM_LEN)
/* 将0x5800040上的镜像拷贝到0x8000,拷贝长度为0x1cbb40 */
| | |--memmove_wd(load_buf, image_buf, image_len, CHUNKSZ);
- 设置setenv fdt_high = 0xffffffff,setenv initrd_high = 0xffffffff,不进行根文件系统和设备树的重定位,节省拷贝时间,参考boot_ramdisk_high和boot_relocate_fdt小节
- 注释掉uboot中对设备树无用的信息传递,找出设备树节点需要耗时
image_setup_libfdt
|--fdt_root(blob) /* 修改序列号serial-number */
|--arch_fixup_fdt(blob) /* gd->bd->bi_dram[bank]修改设备树中的memory节点 */
|--fdt_fixup_ethernet(blob); /* 修改设备树中的mac-address */
|--ft_board_setup(blob, gd->bd); /* 空 */
|--ft_system_setup(blob, gd->bd); /* 空 */
|--fdt_initrd(blob, *initrd_start, *initrd_end); /* 设置chosen中的linux,initrd-start和linux,initrd-end */
Bootm启动流程分析相关推荐
- 解析并符号 读取dll_Spring IOC容器之XmlBeanFactory启动流程分析和源码解析
一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...
- Zygote进程启动流程分析
文中的源代码版本为api23 Zygote进程启动流程分析 先说结论,zygote进程启动过程中主要做了下面这些事情: 启动DVM虚拟机 预加载部分资源,如一些通用类.通用资源.共享库等 启动syst ...
- c++builder启动了怎么停止_App 竟然是这样跑起来的 —— Android App/Activity 启动流程分析...
在我的上一篇文章: AJie:按下电源键后竟然发生了这一幕 -- Android 系统启动流程分析zhuanlan.zhihu.com 我们分析了系统在开机以后的一系列行为,其中最后一阶段 AMS( ...
- SpringBoot启动流程分析(四):IoC容器的初始化过程
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- Exynos4412 Uboot 移植(二)—— Uboot 启动流程分析
uboot启动流程分析如下: 第一阶段: a -- 设置cpu工作模式为SVC模式 b -- 关闭中断,mmu,cache v -- 关看门狗 d -- 初始化内存,串口 e -- 设置栈 f -- ...
- bootloader启动流程分析
bootloader启动流程分析 1.Bootloader的概念和作用 Bootloader是嵌入式系统的引导加载程序,它是系统上电后运行的第一段程序.在完成对系统的初始化任务之后,它会将Flash中 ...
- MyBatis启动流程分析
目录 MyBatis简单介绍 启动流程分析 简单总结 附录 MyBatis内置别名转换 参考 MyBatis简单介绍 MyBatis是一个持久层框架,使用简单,学习成本较低.可以执行自己手写的SQL语 ...
- NameNode之启动流程分析
NameNode启动流程分析 public staticvoid main(Stringargv[]) throws Exception { if (DFSUtil.parseHelpArgument ...
- springboot中获得app_Spring Boot 应用程序启动流程分析
SpringBoot 有两个关键元素: @SpringBootApplication SpringApplication 以及 run() 方法 SpringApplication 这个类应该算是 S ...
最新文章
- Android USB Host与HID通讯
- .NET 大会今日开幕 |这些白嫖福利不看肠子都悔青
- 大数据_Flink_数据处理_运行时架构7_程序结构和数据流图---Flink工作笔记0022
- 雷军是这样评价马斯克的
- 编程中怎样将列表中数字排序_R编程中的列表
- npm和yarn科学设置淘宝镜像
- ubuntu16.04多GPU风扇转速调整
- Windows DLL 注入技术
- yyds,Python爬虫从小白到Bigboss全套学习路线+视频+资料
- 黑苹果E430c, 安装过程
- SpringCloud(一)手把手入门
- RK3399 修改android桌面图标默认大小
- Unity摄像头仿真调研(svl)
- cad2017单段线_CAD中如何绘制多段线
- java Date days_JAVA的Date类与Calendar类(常用方法)
- fortran matlab eng,[转载]关于MATLAB转FORTRAN的几点注意
- 哔哩哔哩2020校园招聘前端笔试卷(一)答案解析
- python(十二)Uiautomator2搭建UI自动化框架实战
- vista识别内存4g_Windows Vista中的语音识别-我在听
- 软件开发常见英文单词