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 [image_addr] [ramdisk_addr] [dtb_addr]
#任何一项都可以没有,如果没有,用 “-’代替
用法1:bootm #使用默认的镜像地址启动
用法2:bootm image_addr #指定镜像地址启动。一般是uImage
用法3:bootm image_addr - dtb_addr #指定设备树地址
# 编译内核时的链接过程
...
LD vmlinux
SORTEX vmlinux
SYSMAP System.map
OBJCOPY arch/arm/boot/Image
Kernel: arch/arm/boot/Image is ready
SHIPPED arch/arm/boot/compressed/hyp-stub.S
SHIPPED arch/arm/boot/compressed/fdt_rw.c
SHIPPED arch/arm/boot/compressed/fdt.h
SHIPPED arch/arm/boot/compressed/libfdt.h
SHIPPED arch/arm/boot/compressed/libfdt_internal.h
SHIPPED arch/arm/boot/compressed/fdt_ro.c
SHIPPED arch/arm/boot/compressed/fdt_wip.c
SHIPPED arch/arm/boot/compressed/fdt.c
SHIPPED arch/arm/boot/compressed/lib1funcs.S
SHIPPED arch/arm/boot/compressed/ashldi3.S
SHIPPED arch/arm/boot/compressed/bswapsdi2.S
LDS arch/arm/boot/compressed/vmlinux.lds
AS arch/arm/boot/compressed/head.o
GZIP arch/arm/boot/compressed/piggy_data
CC arch/arm/boot/compressed/misc.o
CC arch/arm/boot/compressed/decompress.o
CC arch/arm/boot/compressed/string.o
AS arch/arm/boot/compressed/hyp-stub.o
CC arch/arm/boot/compressed/fdt_rw.o
CC arch/arm/boot/compressed/fdt_ro.o
CC arch/arm/boot/compressed/fdt_wip.o
CC arch/arm/boot/compressed/fdt.o
CC arch/arm/boot/compressed/atags_to_fdt.o
AS arch/arm/boot/compressed/lib1funcs.o
AS arch/arm/boot/compressed/ashldi3.o
AS arch/arm/boot/compressed/bswapsdi2.o
AS arch/arm/boot/compressed/piggy.o
LD arch/arm/boot/compressed/vmlinux
OBJCOPY arch/arm/boot/zImage
Kernel: 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
// 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要做的事情
a. 读取头部,把内核拷贝到合适的地方(bootm_headers_t images)
b. 将启动参数给内核准备好,并告诉内核参数的首地址
c. 设置cpu寄存器,禁止中断,关闭MMU和cache
d. 跳转到内核的入口地址,kernel开始运行
#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 memory
passing arguments 'arg ...'; when booting a Linux kernel,
'arg' can be the address of an initrd image
When booting a Linux kernel which requires a flat device-tree
a third argument is required which is the address of the
device-tree blob. To boot that kernel without an initrd image,
use a '-' for the second argument. If you do not pass a third
a bd_info struct will be passed instead
Sub-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 image
ramdisk - relocate initrd, set env initrd_start/initrd_end
fdt - relocate flat device tree
cmdline - OS specific command line processing/setup
bdt - OS specific bd_t processing
prep - OS specific prep before relocation or go
go - 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 # 跳转到内核入口地址
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_HIGH
BOOTM_STATE_RAMDISK |
#endif
BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
BOOTM_STATE_OS_GO, &images, 1);
}
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_HIGH
U_BOOT_CMD_MKENT(ramdisk, 0, 1, (void *)BOOTM_STATE_RAMDISK, "", ""),
#endif
#ifdef CONFIG_OF_LIBFDT
U_BOOT_CMD_MKENT(fdt, 0, 1, (void *)BOOTM_STATE_FDT, "", ""),
#endif
U_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 |
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_()
* 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_HOSTCC
image_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;
#endif
int verify; /* getenv("verify")[0] != 'n' 是否要对image进行验证*/
#ifdef CONFIG_LMB
struct lmb lmb; /* for memory mgmt 用来进行内存管理*/
#endif
} bootm_headers_t;
/*
* @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);
初始化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 0x5800000 - 0x5c00000
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);
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 0x5800000 - 0x5c00000
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);
解压内核镜像,这里指的是制作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));
#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);
}
#endif
int 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
/* 只有执行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);
}
#endif
int 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
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];
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;
设置FDT
设置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);
/* 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;
};
禁止中断,关闭cache,关闭mmu
跳转到内核的入口地址images->ep
#ifdef CONFIG_ARM64
armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0, (u64)switch_to_el1, ES_TO_AARCH64);
armv8_switch_to_el2(0, (u64)gd->bd->bi_arch_number, (u64)images->ft_addr, 0, (u64)images->ep, ES_TO_AARCH32);
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);
以NT98566平台为例:
成员 | 值 |
---|---|
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_print_contents
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);
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)
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);
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 */