赞
踩
目录
https://ke.qq.com/course/4032547?flowToken=1042705
数组static boot_os_fn *boot_os[]
1.sudo apt install u-boot-tools
先看一个命令
- => bdinfo
- arch_number = 0x00000000
- boot_params = 0x80000100
- DRAM bank = 0x00000000
- -> start = 0x80000000
- -> size = 0x20000000
- eth0name = FEC0
- ethaddr = 6c:30:45:2f:91:d7
- current eth = FEC0
- ip_addr = 192.168.0.3
- baudrate = 115200 bps
- TLB addr = 0x9FFF0000
- relocaddr = 0x9FF74000
- reloc off = 0x18774000
- irq_sp = 0x9EF71ED0
- sp start = 0x9EF71EC0
- =>
这个命令就是打印结构体typedef struct bd_info的信息
由-> size 可知当前内存是512MB。
boot_params就是启动参数bootargs存放的地址
看代码,这个函数位于common/board_r.c中, 就是uboot的堆内存初始化函数
- static int initr_malloc(void)
- {
- ulong malloc_start;
-
- #ifdef CONFIG_SYS_MALLOC_F_LEN
- debug("Pre-reloc malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr,
- gd->malloc_ptr / 1024);
- #endif
- /* The malloc area is immediately below the monitor copy in DRAM */
- malloc_start = gd->relocaddr - TOTAL_MALLOC_LEN;
- mem_malloc_init((ulong)map_sysmem(malloc_start, TOTAL_MALLOC_LEN),
- TOTAL_MALLOC_LEN);
- return 0;
- }
打印几个值看看、
printf("malloc_start = 0x%08x len = 0x%08x\n",malloc_start,TOTAL_MALLOC_LEN);
malloc_start + 0x01002000 = 0x9ff74000,正好等于bdinfo命令打印的relocaddr的值,这能说明啥呢?
1.0x9ff74000开始的0x01002000 长度的地址是不能动的。
2.堆内存的长度是16M,知道长度,为修改uboot使用堆内存时提供参考,防止分配失败。
common/dlmalloc.c
该文件中定义可具体的内存初始化和使用函数
- void mem_malloc_init(ulong start, ulong size)
- {
- mem_malloc_start = start;
- mem_malloc_end = start + size;
- mem_malloc_brk = start;
-
- debug("using memory %#lx-%#lx for malloc()\n", mem_malloc_start,
- mem_malloc_end);
- #ifdef CONFIG_SYS_MALLOC_CLEAR_ON_INIT
- memset((void *)mem_malloc_start, 0x0, size);
- #endif
- malloc_bin_reloc();
- }
理论上这里面应该有malloc和free系列函数的,但是没找到,这个有点复杂,它应该是实现了malloc的底层调用的函数,然后用这些函数替换了malloc这个库函数的实现,具体稍后再分析。
这个实际使用的时候,还是使用malloc和free。
环境变量初始化
- static int initr_env(void)
- {
- /* initialize environment */
- if (should_load_env())
- env_relocate();
- else
- set_default_env(NULL);
- #ifdef CONFIG_OF_CONTROL
- setenv_addr("fdtcontroladdr", gd->fdt_blob);
- #endif
-
- /* Initialize from environment */
- load_addr = getenv_ulong("loadaddr", 16, load_addr);
- #if defined(CONFIG_SYS_EXTBDINFO)
- #if defined(CONFIG_405GP) || defined(CONFIG_405EP)
- #if defined(CONFIG_I2CFAST)
- /*
- * set bi_iic_fast for linux taking environment variable
- * "i2cfast" into account
- */
- {
- char *s = getenv("i2cfast");
-
- if (s && ((*s == 'y') || (*s == 'Y'))) {
- gd->bd->bi_iic_fast[0] = 1;
- gd->bd->bi_iic_fast[1] = 1;
- }
- }
- #endif /* CONFIG_I2CFAST */
- #endif /* CONFIG_405GP, CONFIG_405EP */
- #endif /* CONFIG_SYS_EXTBDINFO */
- return 0;
- }
这里面,首先通过should_load_env判断是否已经设置了环境变量,如果设置了就执行,env_relocate环境变量重定位到内存,如果没有设置,就使用uboot编译时配置的默认值。
下面分别看下这两个函数
should_load_env
- /*
- * Tell if it's OK to load the environment early in boot.
- *
- * If CONFIG_OF_CONTROL is defined, we'll check with the FDT to see
- * if this is OK (defaulting to saying it's OK).
- *
- * NOTE: Loading the environment early can be a bad idea if security is
- * important, since no verification is done on the environment.
- *
- * @return 0 if environment should not be loaded, !=0 if it is ok to load
- */
- static int should_load_env(void)
- {
- #ifdef CONFIG_OF_CONTROL
- return fdtdec_get_config_int(gd->fdt_blob, "load-environment", 1);
- #elif defined CONFIG_DELAY_ENVIRONMENT
- return 0;
- #else
- return 1;
- #endif
- }
这里面调用的fdtdec_get_config_int
- int fdtdec_get_config_int(const void *blob, const char *prop_name,
- int default_val)
- {
- int config_node;
-
- debug("%s: %s\n", __func__, prop_name);
- config_node = fdt_path_offset(blob, "/config");
- if (config_node < 0)
- return default_val;
- return fdtdec_get_int(blob, config_node, prop_name, default_val);
- }
这个函数看起来是设备树啊,这是为什么呢,重新看一下,难道环境变量被存放到设备树文件里了吗?而且还是/config节点。真的吗?
测试一下,打印设备树:
使用fdt命令打印了设备树信息,没找到/config节点,可能到后面就明白了,向前分析
set_default_env
Env_common.c (common)
void set_default_env(const char *s)
这个是设置默认环境变量,看看它保存到哪里了,没找到,继续向前看。
env_relocate_spec
这个是和硬件相关的,根据不同的启动设备会调用不同的版本,先放在这。
函数initr_net,先认个门就行了,需要研究的时候再研究。
- static int initr_net(void)
- {
- puts("Net: ");
- eth_initialize();
- #if defined(CONFIG_RESET_PHY_R)
- debug("Reset Ethernet PHY\n");
- reset_phy();
- #endif
- return 0;
- }
static int initr_ethaddr(void)
在这个函数里添加需要修改的值就行了。
Linux_logo.h (include),里面都是数组,这些数组应该就是图片是数据。
该数组中定义了不同的操作系统的启动函数,下图是uboot支持启动的操作系统列表
下面代码是本uboot的启动引导入口
- static boot_os_fn *boot_os[] = {
- [IH_OS_U_BOOT] = do_bootm_standalone,
- #ifdef CONFIG_BOOTM_LINUX
- [IH_OS_LINUX] = do_bootm_linux,
- #endif
- #ifdef CONFIG_BOOTM_NETBSD
- [IH_OS_NETBSD] = do_bootm_netbsd,
- #endif
- #ifdef CONFIG_LYNXKDI
- [IH_OS_LYNXOS] = do_bootm_lynxkdi,
- #endif
- #ifdef CONFIG_BOOTM_RTEMS
- [IH_OS_RTEMS] = do_bootm_rtems,
- #endif
- #if defined(CONFIG_BOOTM_OSE)
- [IH_OS_OSE] = do_bootm_ose,
- #endif
- #if defined(CONFIG_BOOTM_PLAN9)
- [IH_OS_PLAN9] = do_bootm_plan9,
- #endif
- #if defined(CONFIG_BOOTM_VXWORKS) && \
- (defined(CONFIG_PPC) || defined(CONFIG_ARM))
- [IH_OS_VXWORKS] = do_bootm_vxworks,
- #endif
- #if defined(CONFIG_CMD_ELF)
- [IH_OS_QNX] = do_bootm_qnxelf,
- #endif
- #ifdef CONFIG_INTEGRITY
- [IH_OS_INTEGRITY] = do_bootm_integrity,
- #endif
- #ifdef CONFIG_BOOTM_OPENRTOS
- [IH_OS_OPENRTOS] = do_bootm_openrtos,
- #endif
- }
由上可知,do_bootm_linux是linux系统的启动入口
该函数的定义如下所示:
- /* Main Entry point for arm bootm implementation
- *
- * Modeled after the powerpc implementation
- * DIFFERENCE: Instead of calling prep and go at the end
- * they are called if subcommand is equal 0.
- */
- int do_bootm_linux(int flag, int argc, char * const argv[],
- bootm_headers_t *images)
- {
-
- /* No need for those on ARM */
- if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
- return -1;
-
- if (flag & BOOTM_STATE_OS_PREP) {
- boot_prep_linux(images);
- return 0;
- }
-
- if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
- boot_jump_linux(images, flag);
- return 0;
- }
-
- boot_prep_linux(images);
- boot_jump_linux(images, flag);
- return 0;
- }
在该函数开头添加打印信息,观测flag、argc、argv的值,打印如下
- arch/arm/lib/bootm.c,do_bootm_linux,line=358:flag = 256,argc=3
- arch/arm/lib/bootm.c,do_bootm_linux,line=360:argv[0] = 80800000
- arch/arm/lib/bootm.c,do_bootm_linux,line=360:argv[1] = -
- arch/arm/lib/bootm.c,do_bootm_linux,line=360:argv[2] = 86000000
- arch/arm/lib/bootm.c,boot_prep_linux,line=224:using: FDT
-
- Using Device Tree in place at 86000000, end 8600c3fb
- arch/arm/lib/bootm.c,do_bootm_linux,line=358:flag = 1024,argc=3
- arch/arm/lib/bootm.c,do_bootm_linux,line=360:argv[0] = 80800000
- arch/arm/lib/bootm.c,do_bootm_linux,line=360:argv[1] = -
- arch/arm/lib/bootm.c,do_bootm_linux,line=360:argv[2] = 86000000
从打印信息可到以下结果
1.do_bootm_linux被调用了两次
2.do_bootm_linux的argc和argv其实就是bootz的传递过来的参数
3.flag两次值不同,第一次是256即0x100,第二次是10240即0x400
然后再打印5个宏,他们绝对了该函数的if执行分支
- DEBUG_INFO("BOOTM_STATE_OS_BD_T=0x%08x",BOOTM_STATE_OS_BD_T);
- DEBUG_INFO("BOOTM_STATE_OS_CMDLINE=0x%08x",BOOTM_STATE_OS_CMDLINE);
- DEBUG_INFO("BOOTM_STATE_OS_PREP=0x%08x",BOOTM_STATE_OS_PREP);
- DEBUG_INFO("BOOTM_STATE_OS_GO=0x%08x",BOOTM_STATE_OS_GO);
- DEBUG_INFO("BOOTM_STATE_OS_FAKE_GO=0x%08x",BOOTM_STATE_OS_FAKE_GO);
打印值如下:
- arch/arm/lib/bootm.c,do_bootm_linux,line=363:BOOTM_STATE_OS_BD_T=0x00000080
- arch/arm/lib/bootm.c,do_bootm_linux,line=364:BOOTM_STATE_OS_CMDLINE=0x00000040
- arch/arm/lib/bootm.c,do_bootm_linux,line=365:BOOTM_STATE_OS_PREP=0x00000100
- arch/arm/lib/bootm.c,do_bootm_linux,line=366:BOOTM_STATE_OS_GO=0x00000400
- arch/arm/lib/bootm.c,do_bootm_linux,line=367:BOOTM_STATE_OS_FAKE_GO=0x00000200
结合代码,两次值对比,第一次调用时,flag值是0x100进入下下面这个分支
- if (flag & BOOTM_STATE_OS_PREP) {
- boot_prep_linux(images);
- return 0;
- }
boot_prep_linux函数定义如下所示:从第一行代码可知,这个函数是用来处理传递的参数的,这里就是本次分多次编写文档分析的重点所在了。
- 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();
- }
- }
这个函数中有如下一段代码
- int image_setup_linux(bootm_headers_t *images)
- {
- /*略*/
- 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;
- }
- /*略*/
- }
首先,看下这个宏有没有定义,然后确定IMAGE_ENABLE_RAMDISK_HIGH的值,
其次,确认众多的TAG宏,是否被定义,开始写代码验证,列出几个需要验证的宏
- BOOTM_ENABLE_TAGS
- BOOTM_ENABLE_SERIAL_TAG
- BOOTM_ENABLE_CMDLINE_TAG
- BOOTM_ENABLE_REVISION_TAG
- BOOTM_ENABLE_MEMORY_TAGS
- BOOTM_ENABLE_INITRD_TAG
还有需要验证的结构体中从成员,这些值将在下一章,分出一章的内容来分析。
第二次调用时,flag值是0x400进入下面这个分支,
flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)的值就是
0x400 & (0x400 | 0x200) = 0x400,if结果为真
- if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
- DEBUG_INFO("TWO");
- boot_jump_linux(images, flag);
- return 0;
- }
看第一行的注释,Subcommand: GO,go是测试uboot代码时也是执行的go,在uboot中测试裸机代码也是执行的go,uboot启动linux内核时,可能也是go,也是把它当做一个普通的裸机程序对待而已,关键还在于内核自己争气,抓住了机会,一飞冲天。
- /* Subcommand: GO */
- static void boot_jump_linux(bootm_headers_t *images, int flag)
- {
- #ifdef CONFIG_ARM64
- void (*kernel_entry)(void *fdt_addr, void *res0, void *res1,
- void *res2);
- int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
-
- kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,
- void *res2))images->ep;
-
- debug("## Transferring control to Linux (at address %lx)...\n",
- (ulong) kernel_entry);
- bootstage_mark(BOOTSTAGE_ID_RUN_OS);
-
- announce_and_cleanup(fake);
-
- if (!fake) {
- do_nonsec_virt_switch();
- kernel_entry(images->ft_addr, NULL, NULL, NULL);
- }
- #else
- unsigned long machid = gd->bd->bi_arch_number;
- char *s;
- void (*kernel_entry)(int zero, int arch, uint params);
- unsigned long r2;
- int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
-
- kernel_entry = (void (*)(int, int, uint))images->ep;
-
- s = getenv("machid");
- if (s) {
- if (strict_strtoul(s, 16, &machid) < 0) {
- debug("strict_strtoul failed!\n");
- return;
- }
- printf("Using machid 0x%lx from environment\n", machid);
- }
-
- debug("## Transferring control to Linux (at address %08lx)" \
- "...\n", (ulong) kernel_entry);
- bootstage_mark(BOOTSTAGE_ID_RUN_OS);
- announce_and_cleanup(fake);
-
- if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
- r2 = (unsigned long)images->ft_addr;
- else
- r2 = gd->bd->bi_boot_params;
-
- if (!fake) {
- #ifdef CONFIG_ARMV7_NONSEC
- if (armv7_boot_nonsec()) {
- armv7_init_nonsec();
- secure_ram_addr(_do_nonsec_entry)(kernel_entry,
- 0, machid, r2);
- } else
- #endif
- kernel_entry(0, machid, r2);
- }
- #endif
- }
在命令行输入?或者help,就会看到当前uboot支持的命令,这里只关注boot相关的命令
他们对应d_xxx函数例如booz就对应do_bootz函数
- boot - boot default, i.e., run 'bootcmd'
- bootd - boot default, i.e., run 'bootcmd'
- bootelf - Boot from an ELF image in memory
- bootm - boot application image from memory
- bootp - boot image via network using BOOTP/TFTP protocol
- bootvx - Boot vxWorks from an ELF image
- bootz - boot Linux zImage image from memory
看看zImage存放在哪里了
fatls命令,这是的Fat文件系统
- => fatls mmc 1:1
- 6504344 zimage
- 39054 xxx.dtb
-
- 2 file(s), 0 dir(s)
CONFIG_SECURE_BOOT
先知道有这个东西,后面大概率会用到
- => fat
- fatinfo fatload fatls fatsize fatwrite
fatinfo
- => fatinfo
- usage: fatinfo <interface> [<dev[:part]>]
- => fatinfo mmc 1:1
- Interface: MMC
- Device 1: Vendor: Man 000015 Snr af7f8d35 Rev: 0.6 Prod: 8GTF4R
- Type: Removable Hard Disk
- Capacity: 7456.0 MB = 7.2 GB (15269888 x 512)
- Filesystem: FAT32 "NO NAME "
u-boot编译后生成u-boot、u-boot.bin、u-boot.imx三个文件
- $ file u-boot
- u-boot: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /usr/lib/ld.so.1, not stripped
- $ file u-boot.bin
- u-boot.bin: data
- $ file u-boot.imx
- u-boot.imx: data
arm-linux-gnueabihf-objcopy将u-boot文件转换为u-boot.bin文件,然后u-boot.bin可以直接在内存中运行,前面测试时候就是这么做的。
u-boot.imx是下载到flash中存储的,直接在内核中不能运行,它里面包含一些芯片启动时需要加载的参数。就是u-boot.bin再添加一个额外的信息。这个地方每个芯片都不一样。
Autoboot.c (common) 从文件名就知道,它是和自动启动相关的。
const char *bootdelay_process(void)
要修改开启倒数的行为,就修改这里
Command.c (common)
int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp)
这个功能,从代码上看,应该是早期uboot想做一个驱动框架,但是因为驱动本身的多样性和编写驱动的人员的分散性。导致这个计划失败了,我猜可能是这样吧。
- static int initr_jumptable(void)
- {
- jumptable_init();
- return 0;
- }
- void jumptable_init(void)
- {
- gd->jt = malloc(sizeof(struct jt_funcs));
- #include <_exports.h>
- }
关键是给gd->jt赋值。这个变量里面有很多函数指针,估计是为了实现初始化的时候,给这些变量赋值成对应设备的操作函数,想法很好,没有实现。
- stdinname = getenv("stdin");
- stdoutname = getenv("stdout");
- stderrname = getenv("stderr");
配置标准错误、标准输入、标准输出,其实都是对应的串口
这个主要是和软件相关的,如果需要修改调试串口,还是从环境变量那里开始分析好一点。
zImage 是Image的压缩版,并且其前段包含一段未压缩自解压代码。
uboot中mkimage工作,可以将zImage生成uImage,就是在zImage加上64字节的uImage信息。
u-boot启动时,需要uImage,或者zImage,
(这个uboot新版版可能没有了
LINUX_ZIMAGE_MAGIC:这个宏提供zImage启动的支持)
mkimage工具
有两种方法安装这个工具
- $ mkimage
- 程序“mkimage”尚未安装。 您可以使用以下命令安装:
- sudo apt install u-boot-tools
这个感觉不太靠谱,没试
- $ find -name mkimage
- ./tools/mkimage
- lkmao@ubuntu:~/i
把它复制到内核目录下
然后
export PATH=$PATH:./
make uimage,
我在自己的内核试了试,就是这样的,先放在这里,以前看过这个,后来忘记了,所以,这就是不记笔记的坏处。
make: *** No rule to make target 'uimage'。 停止。
The do_bootm() function uses the lzmaBuffToBuffDecopress() function to expand the compressed image.
- The lib_lzma functionality was written by Igor Pavlov. The original source cames from the LZMA SDK web page:
- URL: http://www.7-zip.org/sdk.html Author: Igor Pavlov
- The import is made using the import_lzmasdk.sh script that:
- * untars the lzmaXYY.tar.bz2 file (from the download web page)
- * copies the files LzmaDec.h, Types.h, LzmaDec.c, history.txt, and lzma.txt from source archive into the lib_lzma directory (pwd).
- Example:
- . import_lzmasdk.sh ~/lzma465.tar.bz2
- Notice: The files from lzma sdk are _not modified_ by this script!
- The files LzmaTools.{c,h} are provided to export the lzmaBuffToBuffDecompress() function that wraps the complex LzmaDecode() function from the LZMA SDK. The do_bootm() function uses the lzmaBuffToBuffDecopress() function to expand the compressed image.
- The directory U-BOOT/include/lzma contains stubs files that permit to use the library directly from U-BOOT code without touching the original LZMA SDK's files.
- Luigi 'Comio' Mantellini <luigi.mantellini@idf-hit.com>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。