uboot代码解析4:内核传参、ramdisk、initrd 您所在的位置:网站首页 猜词语比划游戏有哪些名字 uboot代码解析4:内核传参、ramdisk、initrd

uboot代码解析4:内核传参、ramdisk、initrd

2023-08-13 13:30| 来源: 网络整理| 查看: 265

https://ke.qq.com/course/4032547?flowToken=1042705

目录

前言

一 struct tag结构体传参

二 boot_prep_linux函数

三 image_setup_linux函数

四 最后的bootcmd和bootargs值

五 再来看看bootz命令的help信息

六 集成到内核中的RAMDISK传参

七 启动内核

总结

前言

经过前面三篇文章的铺垫,挖了好多坑,终于到了确定内核传参这里了。

一 struct tag结构体传参

uboot是通过struct tag数据结构向内核传递参数的

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

一个struct tag代表一个参数,uboot给内核传递多个struct tag结构体,它的类型由struct tag_header结构体决定,里面包含该tag的类型type和大小size。

struct tag_header { u32 size; u32 tag; };

起始tag和结束tag

uboot向内核传递tag时,并没有传递数量,而是规定了一个起始tag和一个结束tag,并分别由两个函数来做完成这个任务,如下所示

起始tag函数

setup_start_tag(gd->bd); static void setup_start_tag (bd_t *bd) { params = (struct tag *)bd->bi_boot_params; params->hdr.tag = ATAG_CORE; params->hdr.size = tag_size (tag_core); params->u.core.flags = 0; params->u.core.pagesize = 0; params->u.core.rootdev = 0; params = tag_next (params); }

结束tag函数

setup_end_tag(gd->bd); static void setup_end_tag(bd_t *bd) { params->hdr.tag = ATAG_NONE; params->hdr.size = 0; }

从以上代码可知起始tag的类型ATAG_CORE,结束tag是ATAG_NONE

我们重点关注ramdisk和initrd类型。

ramdisk和initrd,看注释,貌似initrd就是压缩的ramdisk。initrd就是init ram disk.

/* describes how the ramdisk will be used in kernel */ #define ATAG_RAMDISK 0x54410004 struct tag_ramdisk { u32 flags; /* bit 0 = load, bit 1 = prompt */ u32 size; /* decompressed ramdisk size in _kilo_ bytes */ u32 start; /* starting block of floppy-based RAM disk image */ };

对应的内核中的位置

Atags_compat.c (arch\arm\kernel): tag->hdr.tag = ATAG_RAMDISK; Atags_parse.c (arch\arm\kernel):__tagtable(ATAG_RAMDISK, parse_tag_ramdisk); Setup.h (arch\arm\include\uapi\asm):#define ATAG_RAMDISK 0x54410004 /* describes where the compressed ramdisk image lives (virtual address) */ /* * this one accidentally used virtual addresses - as such, * its depreciated. */ #define ATAG_INITRD 0x54410005 /* describes where the compressed ramdisk image lives (physical address) */ #define ATAG_INITRD2 0x54420005 struct tag_initrd { u32 start; /* physical start address */ u32 size; /* size of compressed ramdisk image in bytes */ };

对应的内核中的位置

Atags_compat.c (arch\arm\kernel): tag->hdr.tag = ATAG_INITRD; Setup.h (arch\arm\include\uapi\asm):#define ATAG_INITRD 0x54410005 Atags_to_fdt.c (arch\arm\boot\compressed): } else if (atag->hdr.tag == ATAG_INITRD2) { Setup.h (arch\arm\include\uapi\asm):#define ATAG_INITRD2 0x54420005 二 boot_prep_linux函数 /* Subcommand: PREP */ static void boot_prep_linux(bootm_headers_t *images) { char *commandline = getenv("bootargs"); if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) { #ifdef CONFIG_OF_LIBFDT debug("using: FDT\n"); if (image_setup_linux(images)) { printf("FDT creation failed! hanging..."); hang(); } #endif } else if (BOOTM_ENABLE_TAGS) { debug("using: ATAGS\n"); setup_start_tag(gd->bd); if (BOOTM_ENABLE_SERIAL_TAG) setup_serial_tag(¶ms); if (BOOTM_ENABLE_CMDLINE_TAG) setup_commandline_tag(gd->bd, commandline); if (BOOTM_ENABLE_REVISION_TAG) setup_revision_tag(¶ms); if (BOOTM_ENABLE_MEMORY_TAGS) setup_memory_tags(gd->bd); if (BOOTM_ENABLE_INITRD_TAG) { /* * In boot_ramdisk_high(), it may relocate ramdisk to * a specified location. And set images->initrd_start & * images->initrd_end to relocated ramdisk's start/end * addresses. So use them instead of images->rd_start & * images->rd_end when possible. */ if (images->initrd_start && images->initrd_end) { setup_initrd_tag(gd->bd, images->initrd_start, images->initrd_end); } else if (images->rd_start && images->rd_end) { setup_initrd_tag(gd->bd, images->rd_start, images->rd_end); } } setup_board_tags(¶ms); setup_end_tag(gd->bd); } else { printf("FDT and ATAGS support not compiled in - hanging\n"); hang(); } }

通过添加打印信息来分析代码,输出如下信息

IMAGE_ENABLE_OF_LIBFDT=0x00000001,images->ft_len=37884

上面长长等待函数执行的就下面这三行代码,好尴尬,貌似struct tag白看了。从代码结构上看,应该是支持了设备树以后,就不需要struct tag了,这是两种不同的传参方式。

if (image_setup_linux(images)) { printf("FDT creation failed! hanging..."); hang(); } 三 image_setup_linux函数

该函数位于common/image.c文件中,位于#ifdef CONFIG_LMB宏中。lmb的全称是logical memory blocks.

lmb的结构体如下所示

#define MAX_LMB_REGIONS 8 struct lmb_property { phys_addr_t base; phys_size_t size; }; struct lmb_region { unsigned long cnt; phys_size_t size; struct lmb_property region[MAX_LMB_REGIONS+1]; }; struct lmb { struct lmb_region memory; struct lmb_region reserved; }; int image_setup_linux(bootm_headers_t *images) { ulong of_size = images->ft_len; char **of_flat_tree = &images->ft_addr; ulong *initrd_start = &images->initrd_start; ulong *initrd_end = &images->initrd_end; struct lmb *lmb = &images->lmb; ulong rd_len; int ret; if (IMAGE_ENABLE_OF_LIBFDT) boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree); if (IMAGE_BOOT_GET_CMDLINE) { ret = boot_get_cmdline(lmb, &images->cmdline_start, &images->cmdline_end); if (ret) { puts("ERROR with allocation of cmdline\n"); return ret; } } if (IMAGE_ENABLE_RAMDISK_HIGH) { rd_len = images->rd_end - images->rd_start; ret = boot_ramdisk_high(lmb, images->rd_start, rd_len, initrd_start, initrd_end); if (ret) return ret; } if (IMAGE_ENABLE_OF_LIBFDT) { ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size); if (ret) return ret; } if (IMAGE_ENABLE_OF_LIBFDT && of_size) { ret = image_setup_libfdt(images, *of_flat_tree, of_size, lmb); if (ret) return ret; } return 0; }

先添加打印调试信息,免得白分析。

common/image.c,image_setup_linux,line=1362:IMAGE_ENABLE_OF_LIBFDT = 1 common/image.c,image_setup_linux,line=1363:IMAGE_BOOT_GET_CMDLINE = 0 common/image.c,image_setup_linux,line=1364:IMAGE_ENABLE_RAMDISK_HIGH = 1 common/image.c,image_setup_linux,line=1366:of_size = 37884 common/image.c,image_setup_linux,line=1367:images->ft_addr = 0x86000000 common/image.c,image_setup_linux,line=1368:images->initrd_start = 0x00000000 common/image.c,image_setup_linux,line=1369:images->initrd_end = 0x00000000 common/image.c,boot_ramdisk_high,line=1143:## initrd_high = 0xffffffff, copy_to_ram = 0 common/image.c,boot_ramdisk_high,line=1187: ramdisk load start = 0x00000000, ramdisk load end = 0x00000000

从上面可知IMAGE_ENABLE_RAMDISK_HIGH 的是1,initrd_start 和initrd_end的值是0,下一步,给他们传递有效的值。

根据测试,需要分析一个函数boot_get_ramdisk

这个函数中会对ramdisk的格式进行检查

/** * boot_get_ramdisk - main ramdisk handling routine * @argc: command argument count * @argv: command argument list * @images: pointer to the bootm images structure * @arch: expected ramdisk architecture * @rd_start: pointer to a ulong variable, will hold ramdisk start address * @rd_end: pointer to a ulong variable, will hold ramdisk end * * boot_get_ramdisk() is responsible for finding a valid ramdisk image. * Curently supported are the following ramdisk sources: * - multicomponent kernel/ramdisk image, * - commandline provided address of decicated ramdisk image. * * returns: * 0, if ramdisk image was found and valid, or skiped * rd_start and rd_end are set to ramdisk start/end addresses if * ramdisk image is found and valid * * 1, if ramdisk image is found but corrupted, or invalid * rd_start and rd_end are set to 0 if no ramdisk exists */ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, uint8_t arch, ulong *rd_start, ulong *rd_end) { ulong rd_addr, rd_load; ulong rd_data, rd_len; #if defined(CONFIG_IMAGE_FORMAT_LEGACY) const image_header_t *rd_hdr; #endif void *buf; #ifdef CONFIG_SUPPORT_RAW_INITRD char *end; #endif #if defined(CONFIG_FIT) const char *fit_uname_config = images->fit_uname_cfg; const char *fit_uname_ramdisk = NULL; ulong default_addr; int rd_noffset; #endif const char *select = NULL; *rd_start = 0; *rd_end = 0; #ifdef CONFIG_ANDROID_BOOT_IMAGE /* * Look for an Android boot image. */ buf = map_sysmem(images->os.start, 0); if (buf && genimg_get_format(buf) == IMAGE_FORMAT_ANDROID) select = argv[0]; #endif if (argc >= 2) select = argv[1]; /* * Look for a '-' which indicates to ignore the * ramdisk argument */ if (select && strcmp(select, "-") == 0) { debug("## Skipping init Ramdisk\n"); rd_len = rd_data = 0; } else if (select || genimg_has_config(images)) { #if defined(CONFIG_FIT) if (select) { /* * If the init ramdisk comes from the FIT image and * the FIT image address is omitted in the command * line argument, try to use os FIT image address or * default load address. */ if (images->fit_uname_os) default_addr = (ulong)images->fit_hdr_os; else default_addr = load_addr; if (fit_parse_conf(select, default_addr, &rd_addr, &fit_uname_config)) { debug("* ramdisk: config '%s' from image at " "0x%08lx\n", fit_uname_config, rd_addr); } else if (fit_parse_subimage(select, default_addr, &rd_addr, &fit_uname_ramdisk)) { debug("* ramdisk: subimage '%s' from image at " "0x%08lx\n", fit_uname_ramdisk, rd_addr); } else #endif { rd_addr = simple_strtoul(select, NULL, 16); debug("* ramdisk: cmdline image address = " "0x%08lx\n", rd_addr); } #if defined(CONFIG_FIT) } else { /* use FIT configuration provided in first bootm * command argument. If the property is not defined, * quit silently. */ rd_addr = map_to_sysmem(images->fit_hdr_os); rd_noffset = fit_get_node_from_config(images, FIT_RAMDISK_PROP, rd_addr); if (rd_noffset == -ENOLINK) return 0; else if (rd_noffset < 0) return 1; } #endif /* copy from dataflash if needed */ rd_addr = genimg_get_image(rd_addr); /* * Check if there is an initrd image at the * address provided in the second bootm argument * check image type, for FIT images get FIT node. */ buf = map_sysmem(rd_addr, 0); switch (genimg_get_format(buf)) { #if defined(CONFIG_IMAGE_FORMAT_LEGACY) case IMAGE_FORMAT_LEGACY: printf("## Loading init Ramdisk from Legacy " "Image at %08lx ...\n", rd_addr); bootstage_mark(BOOTSTAGE_ID_CHECK_RAMDISK); rd_hdr = image_get_ramdisk(rd_addr, arch, images->verify); if (rd_hdr == NULL) return 1; rd_data = image_get_data(rd_hdr); rd_len = image_get_data_size(rd_hdr); rd_load = image_get_load(rd_hdr); break; #endif #if defined(CONFIG_FIT) case IMAGE_FORMAT_FIT: rd_noffset = fit_image_load(images, rd_addr, &fit_uname_ramdisk, &fit_uname_config, arch, IH_TYPE_RAMDISK, BOOTSTAGE_ID_FIT_RD_START, FIT_LOAD_OPTIONAL_NON_ZERO, &rd_data, &rd_len); if (rd_noffset < 0) return 1; images->fit_hdr_rd = map_sysmem(rd_addr, 0); images->fit_uname_rd = fit_uname_ramdisk; images->fit_noffset_rd = rd_noffset; break; #endif #ifdef CONFIG_ANDROID_BOOT_IMAGE case IMAGE_FORMAT_ANDROID: android_image_get_ramdisk((void *)images->os.start, &rd_data, &rd_len); break; #endif default: #ifdef CONFIG_SUPPORT_RAW_INITRD end = NULL; if (select) end = strchr(select, ':'); if (end) { rd_len = simple_strtoul(++end, NULL, 16); rd_data = rd_addr; } else #endif { puts("Wrong Ramdisk Image Format\n"); rd_data = rd_len = rd_load = 0; return 1; } } } else if (images->legacy_hdr_valid && image_check_type(&images->legacy_hdr_os_copy, IH_TYPE_MULTI)) { /* * Now check if we have a legacy mult-component image, * get second entry data start address and len. */ bootstage_mark(BOOTSTAGE_ID_RAMDISK); printf("## Loading init Ramdisk from multi component " "Legacy Image at %08lx ...\n", (ulong)images->legacy_hdr_os); image_multi_getimg(images->legacy_hdr_os, 1, &rd_data, &rd_len); } else { /* * no initrd image */ bootstage_mark(BOOTSTAGE_ID_NO_RAMDISK); rd_len = rd_data = 0; } if (!rd_data) { debug("## No init Ramdisk\n"); } else { *rd_start = rd_data; *rd_end = rd_data + rd_len; } debug(" ramdisk start = 0x%08lx, ramdisk end = 0x%08lx\n", *rd_start, *rd_end); return 0; }

我在写代码时报了错了,

common/image.c,boot_get_ramdisk,line=978:* ramdisk: cmdline image address = 0x82000000 Wrong Ramdisk Image Format Ramdisk image is corrupt or invalid

这个报错就在下面这行代码

#ifdef CONFIG_SUPPORT_RAW_INITRD end = NULL; if (select) end = strchr(select, ':'); if (end) { rd_len = simple_strtoul(++end, NULL, 16); rd_data = rd_addr; } else #endif { puts("Wrong Ramdisk Image Format\n"); rd_data = rd_len = rd_load = 0; return 1; }

从代码可以看出,它在找一个冒号,冒号后面是结束地址,原来如此,我在传递initrd时,只传递了开始地址,修改bootcmd

setenv bootcmd 'tftp 80800000 zImage;tftp 87000000 emmc.dtb;bootz 80800000 82000000:86000000 87000000;'

这里没有加载ramdisk,因为测试啊,加载ramdisk好慢。先看报错,结果如下:

ERROR: Failed to allocate 0x86000000 bytes below 0x87000000. ramdisk - allocation error FDT creation failed! hanging...### ERROR ### Please RESET the board ###

说明分析对了,然候再次修改bootcmd

setenv bootcmd 'tftp 80800000 zImage;tftp 82000000 emmc.dtb;bootz 80800000 83000000:87000000 82000000;'

在打印信息中看到了如下内容

common/image.c,boot_get_ramdisk,line=978:* ramdisk: cmdline image address = 0x83000000 common/image.c,boot_get_ramdisk,line=1091: ramdisk start = 0x83000000, ramdisk end = 0x0a000000

ramdisk end的值并不是我们想要的87000000,经过分析87000000+83000000 = 10a000000,值刚好溢出所以看到的是0a000000,所以冒号后面是大小,不是结束地址。好吧,又犯了右倾错误了,过度依赖调试信息,没有仔细看代码了。最后成功启动时包含的内核输出信息如下

[ 2.766057] RAMDISK: ext2 filesystem found at block 0 [ 2.771143] RAMDISK: Loading 65536KiB [1 disk] into ram disk... done. [ 5.274650] VFS: Mounted root (ext2 filesystem) on device 1:0. 四 最后的bootcmd和bootargs值

经过一番调试,确定最后的bootcmd和bootargs值如下所示:

setenv bootcmd 'tftp 80800000 zImage;tftp 82000000 emmc.dtb;;tftp 83000000 ram;bootz 80800000 83000000:0x4000000 82000000;' setenv bootargs 'console=ttymxc0,115200 root=/dev/ram0 rootfstype=ext2 rootwait rw' 五 再来看看bootz命令的help信息

再来看下help这个坑吧,这个地方也可以说是,不同的厂家的uboot的实现方式不同,所以最后还是要以代码为主。

=> ? bootz bootz - boot Linux zImage image from memory Usage: bootz [addr [initrd[:size]] [fdt]] - boot Linux zImage stored in memory The argument 'initrd' is optional and specifies the address of the initrd in memory. The optional argument ':size' allows specifying the size of RAW initrd. 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

说下这个帮助信息的信息,booz 后面的参数时可选的initrd后面的size也是可选的。

这样的话,上面得出的结论,就可以重新设置成下面的形式

setenv bootcmd 'tftp 80800000 zImage;tftp 82000000 emmc.dtb;;tftp 83000000 ram;bootz 80800000 83000000 82000000;' setenv bootargs 'console=ttymxc0,115200 ramdisk_size=4000000 root=/dev/ram0 rootfstype=ext2 rootwait rw'

报错了

common/image.c,boot_get_ramdisk,line=978:* ramdisk: cmdline image address = 0x83000000 Wrong Ramdisk Image Format Ramdisk image is corrupt or invalid

为什么会这样呢,得出的结果和help信息冲突啊,其实也不算冲突,我们拿到的uboot基本都不是uboot原版代码,而是由芯片厂家移植过来的,然后再由开发板厂家为了适配自己的开发板经过修改,然后我们再开发板的基础上,做自己的产品开发,所以和官方uboot差距已经很大了,出现这种情况的概率就很大。所以这里体现了分析代码的重要性。

1.在我分析调试代码时,得出的结论是这个uboot版本中:size参数是必须的。

2.我所使用的这个uboot中并没有解析ramdisk_size字符串的代码。

所以最后得出如下 的启动命令和启动参数,而且这个启动命令和启动参数不是可移植的,不同版本uboot,这两个值是不一样的。

setenv bootcmd 'tftp 80800000 zImage;tftp 82000000 emmc.dtb;;tftp 83000000 ram;bootz 80800000 83000000:04000000 82000000;' setenv bootargs 'console=ttymxc0,115200 root=/dev/ram0 rootfstype=ext2 rootwait rw' 六 集成到内核中的RAMDISK传参

详细内容请参考我的另一篇文章。

ramdisk实践1:将根文件系统集成到内核中

setenv bootcmd 'tftp 80800000 zImage;tftp 86000000 emmc.dtb;bootz 80800000 - 86000000;' setenv bootargs 'console=ttymxc0,115200 rootfstype=ramfs rdinit=/linuxrc rootwait rw'

它的启动信息是这样的

[ 5.595239] Warning: unable to open an initial console. /*省略一部分*/ [ 6.983644] random: dd urandom read with 19 bits of entropy available #

这里有一个警告,但是没发现有哪里不不对劲的地方,先战术性忽略它。

七 启动内核

        传递完参数,就开始启动内核了,截取一段打印信息

Booting using the fdt blob at 0x86000000 Using Device Tree in place at 86000000, end 8600c3fb Starting kernel ... -------------------------------------------- [ 0.000000] Booting Linux on physical CPU 0x0

这个打印信息中,横线上面的是uboot打印的,如果内核没起来,可能就不会有下面的信息,横线上面的信息,说明了两个事情

1.uboot是通过设备树而不是struct tag给内核传递参数的

2.设备树的地址是86000000。

总结

       uboot的启动命令和启动参数不具备绝对通用性。但是可以提供参考,然后再具体问题具体分析。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有